分析Ajax爬取今日头条街拍美图

本次目标:以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法。(本文源于借鉴于崔神的网络爬虫实战案例)

1.抓取分析

在抓取之前,首先要分析主权去的逻辑,打开今日头条的首页http://www.toutiao.com/,右上角有搜索入口,这里尝试抓取街拍美图,输入'街拍'搜索,结果如图所示:


这时打开开发者工具,network,xhr选项,如图所示,可以发现一个Ajax请求,继续下拉页面皆可以发现后面的链接不断在温暖过增加,点开第一个 :


通过查看详情,可以发现页面中的数据(图片列表以及标题title)藏在里面:


再往下拉,可以看到标题的相关信息,而我们本次的目的就将图片列表的url以及标题给抓取下来,并且保存在本地,由于这些图片都是以组图的形式呈现出来的,所以保存的时候必须每一组分别保存下来,而我是打算保存的时候,每一组图都建立一个文件夹,文件夹的名称就是该组图的标题。

2.接下来,我们需要直接用Python模拟Ajax请求,将图片的url以及标题title提取出来,但在这之前,我们还需要分析一下URL的 规律,切换回headers选项卡,观察它的URL以及Headers信息,如图:


可以看到这是一个GET请求,请求URL的参数有offset,format,keyword,autoload,count和cur_tab,每一页之间的不同之处就爱在于这个offset,即这个offset就是偏移量,通过改变偏移量的值就可以得到多个Ajax请求信息,等会下面写程序会用到。

3.现在,可以正式说程序了,首先,实现方法get_page()来加载单个Ajax请求的结果,接下来,再实现一个解析方法,提取每条数据的image_list字段中的每一张图片链接,将图片链接和器对应的 标题一并返回,此时可以构成一个生成器,接下来,实现一个保存图片的方法save_image(),该方法中首先根据item中的title来创建文件夹,然后请求这个图片链接 ,获取图片的二进制数据,以二进制的形式写入文件。图片的名称可以使用其内容的MD5值,这样可以去除重复。最后,只需要构建一个offset数组,遍历offset,提取图片链接,并将其下载即可。这里还定义了分页的起始页和终止页,还利用了多线程的线程池,调用其map()方法实现多线程下载。

import requests
from urllib.parse import urlencode
import os
from hashlib import md5
from multiprocessing.pool import Pool




def get_one_page(offset):      #实现方法来加载单个Ajax请求的结果,返回json的字符串格式
    params={
        'offset':offset,
        'format':'json',
        'keyword':'街拍',
        'autoload':'true',
        'count':'20',
        'cur_tab':'1',
    }
    url='https://www.toutiao.com/search_content/?'+urlencode(params)
    try:
        response=requests.get(url)
        if response.status_code==200:
            return response.json()
    except requests.ConnectionError as e:
        return None

def get_images(json):       #实现一个解析方法,提取每条数据的image_list字段中的每一张图片的链接,将图片链接和图片所属的标题一并返回,可以构造一个生成器
    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:
                    yield{
                        'image':'http:'+image.get('url'),
                        'title':title,
                    }
            else:
                return None
#定义一个保存图片的方法,首先根据item的title来创建文件夹,然后请求这个图片链接,获取图片的二进制数据,以二进制的形式写
#入文件。图片的名称可以使用其内容的MD5值,这样可以去除重复。
def save_image(item):
    if not os.path.exists(item.get('title')):
        os.mkdir(item.get('title'))
    try:
        response=requests.get(item.get('image'))
        if response.status_code==200:
            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!')
    except requests.exceptions.MissingSchema as rem:
        print(rem)


def main(offset):
    json=get_one_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=([x*20 for x in range(GROUP_START,GROUP_END+1)])
    pool.map(main,groups)        #利用多线程的线程池,调用其map方法实现多线程下载
    pool.close()
    pool.join()

4.好了,接下来我们可以看下运行结果,如图所示:


好了,今天的内容就到这儿了!

猜你喜欢

转载自blog.csdn.net/coolcooljob/article/details/80365848