算是跟着来的第二个程序,写的时候也意识到很多问题,可能有一两个地方不了解,就回去翻阅相关资料,慢慢的对最开始的程序也做了一些修正,大体上已经熟悉了一整套爬取流程。
先分析各个部分,最后将总体代码贴上去。
一 索引页源码的获取
#返回索引页源码
def get_page_index(offset,keyword):#处理ajax异步加载的数据。
data={
'offset': offset,
'format': 'json',
'keyword': keyword,
'autoload': 'true',
'count': '20',
'cur_tab': 3
}
url='https://www.toutiao.com/search_content/?'+urlencode(data)#直接变为url类型网页,处理异步加载的网页
try:
response=requests.get(url)
if response.status_code ==200:
return response.text
return None
except RequestException:
print('网页打开有误')
return None
首先分析爬取框架,首先知道所要爬取页面是由Ajax请求决定的,下拉时网址后面的offset值会变化,所以决定了对网页解析时要注意处理这个地方,在这里用到了urllib.parse中的urlencode模块,进行了网址的拼接,使url可以被正常访问,offset留给main函数中去提供。
二 对索引页的解析
def parse_page_index(html):
try:
data=json.loads(html)#json格式与python格式互相转化
if data and 'data'in data.keys():
for item in data.get('data'):
yield item.get('article_url')
except JSONDecodeError:
pass
因为存在js内容,所以先将源码利用json.loads()方法转换为python形式的数据,观察到在得到的字典中,‘data’键所对应的值中包含了详情页的网址链接,所以利用函数生成器yield方法循环取得详情页的网址,总体循环写在main函数中。
三 详情页源码获取以及解析
def get_page_detail(url):
try:
response=requests.get(url)
if response.status_code ==200:
return response.text
return None
except RequestException:
print('详情页有误')
return None
def parse_page_detail(html,url):#解析详情页
soup=BeautifulSoup(html,'lxml')
title=soup.select('title')[0].get_text()
#print(title)
images_pattern=re.compile(r'gallery: JSON.parse\("(.*?)"\),',re.S)#前面一般加r,避免转义错误
result=re.search(images_pattern,html)
if result:#建立在找得到左右划切换图片的网址基础上,否则不返回
result=re.sub(r'\\','',result.group(1))
data=json.loads(result)#result为json对象
if data and 'sub_images' in data.keys():
sub_images=data.get('sub_images')
images=[item.get('url') for item in sub_images]#取得一个图片网址列表
if images:
for image in images:download_image(image)
data={
'title':title,
'url':url,
'images':images
}
return data
else:return '0'
源码返回没什么好看的,关键是对详情页图片的提取,经过f12检查后,我们发现所有的图片地址都存放在gallery: JSON.parse()这个括号里面,但是没法简单的提取,这时候我们试图用正则表达式来对图片网址进行提取。注意加上转移符号,对\也进行替换处理,最后将该json对象用loads方式赚啊还,找到’sub_images’,用for循环取得图片网址链表,利用图片下载函数将图片下载到本地,并返回字典,以便于储存在mongodb数据库中。接下来是总体代码。
from urllib.parse import urlencode
from bs4 import BeautifulSoup
from json.decoder import JSONDecodeError
from requests.exceptions import RequestException
import re
import requests
import pymongo
from config import *
import os
from hashlib import md5
from multiprocessing import Pool
import json
client=pymongo.MongoClient(MONGO_URL)
ceshi=client['toutiao']
final_data=ceshi['toutiao1']
#返回索引页源码
def get_page_index(offset,keyword):#处理ajax异步加载的数据。
data={
'offset': offset,
'format': 'json',
'keyword': keyword,
'autoload': 'true',
'count': '20',
'cur_tab': 3
}
url='https://www.toutiao.com/search_content/?'+urlencode(data)#直接变为url类型网页,处理异步加载的网页
try:
response=requests.get(url)
if response.status_code ==200:
return response.text
return None
except RequestException:
print('网页打开有误')
return None
#索引页解析,yield返回详情页连接
def parse_page_index(html):
try:
data=json.loads(html)#json格式与python格式互相转化
if data and 'data'in data.keys():
for item in data.get('data'):
yield item.get('article_url')
except JSONDecodeError:
pass
#返回详情页源码
def get_page_detail(url):
try:
response=requests.get(url)
if response.status_code ==200:
return response.text
return None
except RequestException:
print('详情页有误')
return None
def parse_page_detail(html,url):#解析详情页
soup=BeautifulSoup(html,'lxml')
title=soup.select('title')[0].get_text()
#print(title)
images_pattern=re.compile(r'gallery: JSON.parse\("(.*?)"\),',re.S)#前面一般加r,避免转义错误
result=re.search(images_pattern,html)
if result:#建立在找得到左右划切换图片的网址基础上,否则不返回
result=re.sub(r'\\','',result.group(1))
data=json.loads(result)#result为json对象
if data and 'sub_images' in data.keys():
sub_images=data.get('sub_images')
images=[item.get('url') for item in sub_images]#取得一个图片网址列表
if images:
for image in images:download_image(image)
data={
'title':title,
'url':url,
'images':images
}
return data
else:return '0'
def save_to_mongo(result):
if final_data.insert_one(result):
print('存储成功',result)
return True
return False
def download_image(url):
try:
response=requests.get(url)
if response.status_code ==200:
print('正在下载',url)
save_image(response.content)#返回二进制,图片用
return None
except RequestException:
print('图片下载错误')
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)
for url in parse_page_index(html):#对现在网页上的每个子网页进行遍历,已经开始循环
page_html=get_page_detail(url)
if page_html:
result=parse_page_detail(page_html,url)
'''if result!='0':
save_to_mongo(result)'''
if __name__=='__main__':
groups=[x*20 for x in range(GROUP_START,GROUP_END+1)]
pool=Pool()
pool.map(main,groups)
这是配置文件
MONGO_URL='localhost:27017'
GROUP_START = 1
GROUP_END = 19
KEYWORD='街拍'