抓取ajax网站可以通过分析ajax接口的方式获取到返回的json数据,从而抓取到我们想要的数据,以今日头条为例,如何分析ajax接口,模拟ajax请求爬取数据。
以今日头条的街拍为例,网页上一页只显示部分数据,查看后续数据需要鼠标下滑,这里我们分析一下它的ajax接口。
打开开发者工具,选择network,点击XHR过滤出来ajax请求,可以看到这里有很多参数,其中一眼能看出来的参数就是keyword,是我们搜索的关键字。接着看它的Preview信息。
Preview信息中可以看到包含有很多data信息,点开可以看到其中包含许多有用的信息,像街拍标题,图片地址。
当鼠标往下滑动,过滤出来的ajax请求会多出来一条,如下所示
可以看到其中的offset参数由0变成了20,细心观察网页可以发现网页显示的信息一页恰好是20条。
这是当鼠标滑动到第三页的时候,可以看到offset参数变成了40,当第一页的时候,offset参数为0,第二页offset参数为20,第三页参数为40。这就不难发现offset参数实际是偏移量,用来实现翻页的参数。那我们就可以通过urlencode方法将这些参数拼接到url后面,发起ajax请求,通过控制传入的offset参数来控制翻页,然后通过response.json()来获取网页返回的json数据。
代码思路:1.分析网页的ajax接口,需要传入哪些数据2.通过urlencode键参数拼接到需要请求的url之后,通过控制offset参数来指定爬取哪一页的内容。3.生成不同页的请求,获取json数据中图片的url信息4.请求图片的url,下载图片5.保存到文件夹。
实际代码
import requests
from urllib.parse import urlencode,urljoin
import os
from hashlib import md5
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36",
"X-Requested-With":"XMLHttpRequest"
}
def get_page(offset):
"""
:param offset: 偏移量,控制翻页
:return:
"""
params = {
"offset":offset,
"format":"json",
"keyword":"街拍",
"autoload":"true",
"count":"20",
"cur_tab":"1",
"from":"search_tab"
}
url = "https://www.toutiao.com/search_content/?" + urlencode(params)
try:
response = requests.get(url,headers=headers,timeout=5)
if response.status_code == 200:
return response.json()
except requests.ConnectionError as e:
return None
def get_image(json):
"""
:param json: 获取到返回的json数据
:return:
"""
if json:
for item in json.get("data"):
title = item.get("title")
images = item.get("image_list")
if images:
for image in images:
yield {
"title":title,
"image":urljoin("http:",image.get("url")) if type(image) == type({"t":1}) else urljoin("http:",image)
}
def save_images(item):
"""
将图片保存到文件夹,以标题命名文件夹
:param item: json数据
:return:
"""
if item.get("title") != None:
if not os.path.exists(item.get("title")):
os.mkdir(item.get("title"))
else:
pass
try:
response = requests.get(item.get("image"))
if response.status_code == 200:
file_path = "{0}/{1}.{2}".format(item.get("title") if item.get("title") != None else "Notitle",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 Download Image")
def main(offset):
"""
控制爬取的主要逻辑
:param offset: 偏移量
:return:
"""
json = get_page(offset)
for item in get_image(json):
print(item)
save_images(item)
groups = [i*20 for i in range(1,10)]
if __name__ == '__main__':
for group in groups:
main(group)
爬取的成果
通过分析ajax接口,模拟ajax请求进行抓取相对于selenium模拟来说较简单,但是代码复用性差,因为每个网页的接口都是不同的,所以抓取ajax加载的数据时,使用selenium模拟还是直接抓取接口数据需要小伙伴自己通过实际需要来选择。