以今日头条为例通过分析Ajax请求来抓取网页数据的方法。抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地保存下来
打开连接:http://www.toutiao.com/search/?keyword=街拍
网页内容是由Ajax加载,然后用JavaScript渲染出来的,我们打开开发者工具,切换到XHR过滤选项卡,查看Ajax请求。
查看是否包含了页面中的相关数据。
切换到Headers选项卡,刷新观察请求URL和Headers信息,这是一个GET请求,请求URL的参数有offset, format, keyword, autoload, count 和 cur_tab 。我们需要找出这些参数的规律
接下来,继续滑动页面,多加载一些数据,加载的同时,Network中又出现了许多Ajax请求
这里观察一下后续的参数,发现发生变化的只有offset,是url链接唯一变化的参数,其他参数都没有变化。第二次请求的offset为20,第三次40,第四次60,所以可以发现规律,这个offset就是偏移量,推断出count参数就是一次性获取的数据条数,我们可以利用offset参数来控制分页。
这样一来就可以通过接口批量获取数据了,然后将数据解析,将图片下载下来。
运行之后效果图,街拍美图都分文件夹保存下来了
源代码如下:
import requests
from urllib.parse import urlencode
import os
from hashlib import md5
from multiprocessing.pool import Pool
# 加载单个Ajax请求结果
# 其中唯一变化的就是offset,所以我们将它当做参数传递
def get_page(offset):
# url链接参数
params={
'offset':offset,
'format':'json',
'keyword':'街拍',
'autoload':'true',
'count':'20',
'cur_tab':'1',
'from':'search_tab',
'pd':'synthesis'
}
# urlencode()方法构造请求的GET参数
url='https://www.toutiao.com/search_content/?'+urlencode(params)
try:
# request请求url
response=requests.get(url)
# 是否请求成功
if response.status_code == 200:
# 调用response的json()方法将结果转为json格式
return response.json()
except requests.ConnectionError:
return None
# 实现解析方法
def get_images(json):
if json.get('data'):
for item in json.get('data'):
# 获取图片所属标题
title=item.get('title')
# 获取图片列表
images=item.get('image_list')
if images:
for image in images:
# 实现一个生成器,提取每条数据的image_detail字段中的每一张图片链接和图片所属标题
yield {
'image':image.get('url'),
'title':title
}
# 实现保存图片的方法
def save_image(item):
if not os.path.exists(item.get('title')):
# 根据item的title来创建文件夹
os.mkdir(item.get('title'))
try:
# 请求图片链接,然后获取图片的二进制数据
response=requests.get('http:'+item.get('image'))
if response.status_code == 200:
# 图片的名称可以使用其内容的md5值,这样可以去重
file_path='{0}/{1}.{2}'.format(item.get('title'),md5(response.content).hexdigest(),'jpg')
if not os.path.exists(file_path):
with open(file_path,'wb') as f:
f.write(response.content)
else:
print('Already Downloaded',file_path)
except requests.ConnectionError:
print('Failed to Save Image')
def main(offset):
json=get_page(offset)
# 提取图片链接,并将其下载
for item in get_images(json):
print(item)
save_image(item)
# 起始页
GROUP_START = 1
# 终止页数
GROUP_END = 20
if __name__ == '__main__':
# 构造线程池
pool=Pool()
# groups是url链接唯一变化的参数,第二次请求的offset为20,第三次40,第四次60
# 所以可以发现规律,这个offset就是偏移量,推断出count参数就是一次性获取的数据条数
# 我们可以利用offset参数来控制分页
groups=([x * 20 for x in range(GROUP_START,GROUP_END+1)])
# 调用map()方法实现多线程下载
pool.map(main,groups)
pool.close()
pool.join()