配置文件
MONGO_URL='localhost' #链接地址
MONGO_DB='toutiao' #数据库名称
MONGO_TABLE='toutiao' #表名
group_start=1 #起始点
group_end=200 #终点
keyword='街拍'
主程序
# -*- coding: utf-8 -*
import requests
import json
import re
from urllib.parse import urlencode #urlencode模块导入
from requests.exceptions import RequestException #异常代码模块
from bs4 import BeautifulSoup
from config import * #注意要把config.py文件放在环境变量的目录下
import pymongo
import os
from hashlib import md5 #md5值的判断模块
from multiprocessing import Pool #多进程模块
#from json.decoder import JOSNDecodeError
from simplejson.scanner import JSONDecodeError
client=pymongo.MongoClient(MONGO_URL,connect=False) #声明MongoDB对象
db=client[MONGO_DB] #定义db
def get_page_index(offset,keyword): #抓取索引页内容
data={
'offset': offset, #offset是可变的
'format': 'json',
'keyword': keyword,#keyword是可以定义的
'autoload':'true',
'count': '20',
'cur_tab': 1,
'from':'search_tab'
}
#data是从XHR项目返回结果的Headers>Query String Parameters里的数据,Query String Parameters指的就是通过在URL中携带的方式提交的参数,也就是PHP中$_GET里的参数
url='https://www.toutiao.com/search_content/?'+urlencode(data) #urlencode可以把字典对象变成url的请求参数
try:
response=requests.get(url) #请求url
if response.status_code==200:
return response.text
return None
except RequestException: #所有requests的异常
print('请求索引页出错')
return None
def parse_page_index(html):#解析索引页
try:
data=json.loads(html) #对字符串进行解析,把字符串转化成json对象
if data and 'data' in data.keys(): #判断json数据里面有data
for item in data.get('data'): #
yield item.get('article_url') #构造一个生成器,把所有的article_url解析出来
except JOSNDecodeError: #判断网页为空的时候
pass
def get_page_detati(url):#抓取详情页内容
headers= {
'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding':'gzip, deflate, br',
'accept-language':'zh-CN,zh;q=0.9',
'cache-control':'max-age=0',
'referer':'https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D',
'upgrade-insecure-requests':'1',
'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5083.400 QQBrowser/10.0.988.400'
}
try:
response=requests.get(url,headers=headers)
if response.status_code==200:
return response.text
return None
except RequestException:
print('请求详情页出错',url)
return None
def parse_page_detail(html,url): #解析子页面
try:
soup=BeautifulSoup(html,'lxml') #用BeautifulSoup来解析获取的子页面html代码
#print(soup)
titie=soup.select('title')[0].get_text() #CSS选择器,选标签里面的文本信息
print(titie)
images_pattern=re.compile('gallery: (.*?),\n',re.S) #用正则来匹配出gallery里面的数据
result=re.search(images_pattern,html) #传入正则表达式对象
data1 = result.group(1)
data2=data1[12:-2] #去除没用的字符串
data2=eval("'{}'".format(data2)) #去除双斜杠
data = json.loads(data2)
if data and 'sub_images' in data.keys(): #判断里面是否含有我们想要的数据
sub_images=data.get('sub_images')
images=[item.get('url') for item in sub_images]#列表的创建,每一个sub_images里面的每一个item里面的子元素
for image in images:download_imagg(image) #循环下载所有的图片
return {
'title':titie,
'url':url,
'images':images
}
except AttributeError: #判断josn空的时候
pass
def save_to_mongo(result): #把字典传入mongodb
if db[MONGO_TABLE].insert(result): #如果插入数据成功
print('存储成功到MongoDB',result)
return True
return False
def download_imagg(url): #图片下载
print('正在下载',url)
try:
response=requests.get(url)
if response.status_code==200:
#return response.text
save_image(response.content) #content 返回二进制内容 ,图片一般都是返回content
return None
except RequestException:
print('请求图片出错',url)
return None
def save_image(content):
file_path='{0}/{1}.{2}'.format(os.getcwd(),md5(content).hexdigest(),'jpg')
if not os.path.exists(file_path):
with open(file_path,'wb')as f:
f.write(content)
f.close()
def main(offset):
html = get_page_index(offset,keyword)
#print(html)
for url in parse_page_index(html):#利用for循环,提取出所有的article_url得到详情页网址
html=get_page_detati(url) #将提取到的url 传#解析索引页
#print(url)
#print(html)
if html:
#parse_page_detail(html)
result=parse_page_detail(html,url)
#save_to_mongo(result)
if result:save_to_mongo(result)
if __name__ == '__main__':
#main()
groups=[ x * 20 for x in range(group_start,group_end + 1)]
pool=Pool() #进程值
pool.map(main,groups)#开启多进程