本节我们以今日头条为例,尝试通过分析Ajax请求爬取网页数据。这次抓拍的目标是今日头条的街拍。抓拍完成后,将每组图片下载到文件夹中,保存到本地。小编创建了一个2000人的python交流群。有零基础和工作的朋友可以交流,还有相关的电子书和视频下载。欢迎正在学习python的朋友和已经工作的朋友一起进来交流哦!群:877169862准备工作在开始本节之前,请确保已经安装了requests库。如果没有安装,可以参考第一章爬虫分析在爬虫之前,首先要分析爬虫的逻辑。打开今日头条首页http://www.toutiao.com/,如图6-15所示。右上角有搜索入口。这里我们尝试拍摄漂亮的街拍照片,所以输入“街拍”一词进行搜索,结果如图6-16所示。这时候打开开发者工具,查看所有的网络请求。首先,打开第一个网络请求,本次请求的URL为当前链接http://www.toutiao.com/search...,打开Preview标签查看ResponseBody。如果页面上的内容是根据第一个请求得到的结果渲染的,那么第一个请求的源代码必须包含页面结果中的文本。为了验证,我们可以尝试搜索搜索结果的标题,比如“路人”这个词,如图6-17所示。我们发现网页源代码中并没有包含这两个词,搜索匹配结果数为0,因此可以初步判断这些内容是通过Ajax加载然后通过JavaScript渲染的。接下来,我们可以切换到XHR过滤器选项卡,查看是否有任何Ajax请求。正如预期的那样,这里出现了一个更通用的Ajax请求,以查看其结果是否包含来自页面的相关数据。点击数据域展开,发现这里有很多条数据。点击第一项展开,可以发现有一个title字段,它的值就是页面上第一项数据的标题。再查看其他数据,恰好是一一对应的,如图6-18所示。这证实了数据确实是由Ajax加载的。我们的目的是捕捉美丽的画面。这里,一组图片对应于前一个数据域中的一条数据。每条数据还有一个image_detail字段,它是一个列表的形式,里面包含了一组图片中所有图片的列表,如图6-19所示。因此,我们只需要将列表中的url字段提取出来,下载即可。为每组图建立一个文件夹,文件夹的名字就是组图的标题。接下来可以直接用Python模拟这个Ajax请求,然后提取相关的美图链接下载。但在此之前,我们需要分析一下URL的规则。切换回Headers选项卡,观察其请求URL和Headers信息,如图6-20所示。可以看出这是一个GET请求,请求URL的参数包括offset、format、keyword、autoload、count、cur_tab。我们需要找出这些参数的规律,因为它可以很方便地被程序构造出来。接下来,您可以滑动页面以加载更多新结果。加载时,可以发现Network中有很多Ajax请求,如图6-21所示。这里我们观察后续链接的参数,发现只有offset参数变化,其他参数没有变化,第二次请求的offset值是20,第三次是40,第四次是60,所以我们可以发现规律,这个offset值就是偏移量,由此可以推断count参数就是一次要获取的数据条数。因此,我们可以使用offset参数来控制数据分页。这样我们就可以通过接口批量获取数据,然后分析数据,下载图片。实战练习我们刚刚分析了Ajax请求的逻辑,下面我们就用程序来实现美图的下载吧。首先,实现方法get_page()以加载单个Ajax请求的结果。唯一改变的参数是偏移量,所以我们将它作为参数传递,如下所示:':'街拍','autoload':'true','count':'20','cur_tab':'1',}url='http://www.toutiao.com/search_content/?'urlencode(params)try:response=requests.get(url)ifresponse.status_code==200:returnresponse.json()exceptrequests.ConnectionError:returnNone这里我们使用urlencode()方法构造GET参数request,然后使用requests请求这个链接,如果返回状态码是200,调用response的json()方法将结果转成json格式,然后返回。接下来实现一个解析方法:提取每条数据的image_detail字段中的每一个图片链接,将图片链接和图片标题一起返回。这时候就可以构造一个生成器了。实现代码如下:defget_images(json):ifjson.get('data'):foriteminjson.get('data'):title=item.get('title')images=item.get('image_detail')forimageinimages:yield{'image':image.get('url'),'title':title}接下来,实现一个保存图像的saveimage()方法,其中项目由以前的getimages()方法的字典。该方法首先根据item的标题创建文件夹,然后请求图片链接,获取图片的二进制数据,以二进制形式写入文件。图片的名称可以使用其内容的MD5值,这样可以去除重复。相关代码如下:importosfromhashlibimportmd5defsave_image(item):ifnotos.path.exists(item.get('title')):os.mkdir(item.get('title'))try:response=requests.get(item.get('image'))如果response.status_code==200:file_path='{0}/{1}.{2}'.format(item.get('title'),md5(response.content).hexdigest(),'jpg')如果不是os.path.exists(file_path):withopen(file_path,'wb')asf:f.write(response.content)else:print('AlreadyDownloaded',file_path)exceptrequests.ConnectionError:print('FailedtoSaveImage')最后只需要构造一个偏移量数组,遍历偏移量,提取图片链接,下载:frommultiprocessing.poolimportPooldefmain(offset):json=get_page(offset)foriteminget_images(json):print(item)save_image(item)GROUP_START=1GROUP_END=20if__name__=='__main__':pool=Pool()组=([x*20forx在范围内(GROUP_START,GROUP_END1)])pool.map(main,groups)pool.close()pool.join()这里定义了分页的起始页码和结束页码,分别是GROUPSTART和GROUPEND,也是利用了多线程线程pool,调用它的map()方法实现多线程下载,整个程序就完成了。运行后可以发现街拍图片保存在文件夹中,如图6-22所示。通过这一节,我们了解了Ajax解析的过程,Ajax分页的模拟和下载图片的过程。这部分内容需要熟练掌握,后面的实战中会多次使用这种分析捕获。
