python爬虫—分析Ajax请求对json文件爬取今日头条街拍美图html
本次抓取目标是今日头条的街拍美图,爬取完成以后,将每组图片下载到本地并保存到不一样文件夹下。下面经过抓取今日头条街拍美图讲解一下具体操做步骤。python
网站:
https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D
环境:anaconda3-spyderweb
一些网页直接请求获得的HTML代码其实并无咱们在网页中看到的内容,这是由于一些信息经过Ajax加载了,而后经过JavaScript渲染生成的,这时就须要经过分析网页的请求来获取想要爬取的内容。例如我进入街拍这个页面下,右键查看网页源代码,再返回街拍页面一直向下拉动,街拍页面会变换出现不一样的内容和图片,再右键查看源代码,发现源代码和第一次咱们查看的源代码是同样的,没有发生变化,而且源代码很短。以下所示:
算法
因此重点来了,想看数据在哪,咱们如何定位呢?
一、Chrome查看的Ajax请求
打开开发者工具,查看全部的网络请求
二、查看URL内的数据
咱们再data下面可以找到title
还可以找到image_list,包含图片的连接,也就是咱们要爬取的图片url。所以,咱们只须要将列表中的url字段提取出来并下载就ok了,每一组图都创建一个文件夹,文件夹的名称就为组图的标题。json
三、请求URL和Headers信息找规律
属性位置的定位找到了之后,咱们还需分析一下URL的变化规律。点击headers,观察它的请求URL和Headers信息,查看第一个headers,红框里确实包含不少参数
这么多参数不用蒙圈,由于都在下面显示好了api
如今咱们查看第二个headers的url参数,发现有一个参数发生了变化,offest由0变成20网络
咱们再观察第三个headers的url的参数
offest变成了40,因此不难发现,除了offest之外,其余参数都是不变的,只有offest发生变化,而且变化的规律为0、20、40、60…这样的规律。因此说这个offset值就是偏移量,进而能够推断出页面每加载一次,获取的数据条数为20。所以,咱们能够用offset参数来控制数据分页,经过这个接口批量获取数据了,而后解析。app
第一个包,主要目的是经过from urllib.parse import urlencode构造完整的URL。Urllib是python内置的HTTP请求库,urllib.parse url解析模块。
第四个os模块主要和文件路径相关联
第五个把字符串换成MD5的方法python爬虫
from urllib.parse import urlencode import requests import json import os from hashlib import md5
定义一个函数get_page()来加载单个Ajax请求的结果。由上述分析可知惟一变化的参数就是offset,因此咱们将它看成参数传递进来。若是访问成功,状态码为200,最后返回的就是json格式的解析页面
svg
def get_page(offest):#获取请求并返回解析页面 params = { 'aid': '24', 'app_name': 'web_search', 'offset': offest, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'en_qc': '1', 'cur_tab': '1', 'from': 'search_tab', 'pd': 'synthesis', 'timestamp': '1602563085392', '_signature': 'LfFmxgAgEBBOl5xAwmvANy3wJ9AAHJ7m7oodN5ACyp.Hg2l3uBmMDqDDDN6rWnqjaV0akHlvj327I-FN9Xrxz9FT7hdlRtmAVGnGPJlX5zzT8tcamaIq51QEsm2ry1w0eu0' } url = base_url + urlencode(params) try: rq = requests.get(url,headers = headers) if rq.status_code == 200 : #print(rq.json()) return rq.json() except rq.ConnectionError as e: print('程序错误',e.args)
html为上面函数解析出来的json页面信息,咱们做为参数传进这个函数中,需先找到html下的data,再从data下找title和image_list.
def get_images(html): if html.get('data'): #获得data下的所有内容 for item in html.get('data'): #用item循环每一条 #这里须要判断image_list是否为空 title = item.get('title') if 'image_list' in item and item['image_list'] != []: images=item['image_list'] for image in images: yield{ 'image': image.get('url'), 'title': title }#返回一个字典
item为data下的全部内容
若是文件夹不存在,那么就os.mkdir新建一个以title为名的文件夹,item.get(‘title’)中括号里的title是上面那个函数返回的
‘image’: image.get(‘url’),
‘title’: title
把它定义成了title
若是咱们成功访问了图片的连接地址,那么就能够图片命名了,这里用了哈希算法(我下去还得好好再理解一下)。接下来若是文件夹里面没东西,就打开文件夹写入图片response.content。
def save_image(item): #os.path模块主要用于文件的属性获取,exists是“存在”的意思, #因此顾名思义,os.path.exists()就是判断括号里的文件夹'picture'+str(offset)是否存在的意思,括号内的能够是文件路径。 if not os.path.exists(item.get('title')):#判断当前文件夹下是否有该文件 os.mkdir(item.get('title'))#若是不存在就建立该文件夹 try: response=requests.get(item['image']) #get函数获取图片连接地址,requests发送访问请求,上面那个字典 if response.status_code==200: file_path='{0}/{1}.{2}'.format(item.get('title'),md5(response.content).hexdigest(),'jpg') # md5摘要算法(哈希算法),经过摘要算法获得一个长度固定的数据块。将文件保存时,经过哈希函数对每一个文件进行文件名的自动生成。 # md5() 获取一个md5加密算法对象 # hexdigest() 获取加密后的16进制字符串 if not os.path.exists(file_path): with open(file_path,'wb') as f: f.write(response.content) print('Downloaded image path is: ', file_path) else: print('Already Dowloaded',file_path) except requests.ConnectionError: print('Failed to Save Image')
先存如两页的图片
if __name__ == '__main__': base_url = 'https://www.toutiao.com/api/search/content/?' headers = { 'referer': 'https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36', 'x-requested-with': 'XMLHttpRequest' } for offest in range(0,40,20): html = get_page(offest) a=get_images(html) for item in a: save_image(item) for i in a: print(i)
我把存入的过程依次输出
查看文件!