zoukankan      html  css  js  c++  java
  • 爬虫—分析Ajax爬取今日头条图片

      以今日头条为例分析Ajax请求抓取网页数据。本次抓取今日头条的街拍关键字对应的图片,并保存到本地

    一,分析

      打开今日头条主页,在搜索框中输入街拍二字,打开开发者工具,发现浏览器显示的数据不在其源码里面。这样可以出初步判断这些内容是由

    Ajax加载,然后使用JavaScript渲染出来的。

            

      切换到XHR过滤选项卡,查看其Ajax请求。点击其中一条进去,进入data展开,发现其中一个title字段对应的值正好是页面中的某条数据的标题。再查看其他数据,正好也是一一对应的,这说明这些数据确实是由Ajax加载的。

             

      

      本次的目的是抓取其中的图片内容,data中每个元素就是一篇文章,元素中的image_list字段包含了该文章的图片内容。它是一个列表形式,包含了所有的图片列表。我们只需要将列表中的url字段下载下来就好了,每篇文章都创建一个文件夹,文件夹名称即文章标题。

              

      在使用Python爬取之前还需要分析一下URL的规律。切换到Headers选项卡,查看Headers信息。可以看到,这是一个GET请求,请求的参数有aid,app_name,offset,format,keyword,autoload,count,en_qc,cur_tab,from,pd,timestamp。继续往下滑动,多加载一些数据,找出其中的规律。

            

      经过观察,可以发现变化的参数只有offset,timestamp。第一次请求的offset的值为0,第二次为20,第三次为40,key推断出这个offset就是偏移量,count为每次请求的数据量,而timestamp为时间戳。这样一来,我们就可以使用offset参数控制分页了,通过模拟Ajax请求获取数据,最后将数据解析后下载即可。

    二,爬取

      刚才已经分析完了整个Ajax请求,接下来就是使用代码来实现这个过程。

    # _*_ coding=utf-8 _*_
    
    import requests
    import time
    import os
    from hashlib import md5
    from urllib.parse import urlencode
    from multiprocessing.pool import Pool
    
    
    def get_data(offset):
        """
        构造URL,发送请求
        :param offset:
        :return:
        """
        timestamp = int(time.time())
        params = {
            'aid': '24',
            'app_name': 'web_search',
            'offset': offset,
            'format': 'json',
            'autoload': 'true',
            'count': '20',
            'en_qc': '1',
            'cur_tab': '1',
            'from': 'search_tab',
            'pd': 'synthesis',
            'timestamp': timestamp
        }
    
        base_url = 'https://www.toutiao.com/api/search/content/?keyword=%E8%A1%97%E6%8B%8D'
        url = base_url + urlencode(params)
        try:
            res = requests.get(url)
            if res.status_code == 200:
                return res.json()
        except requests.ConnectionError:
            return '555...'
    
    
    def get_img(data):
        """
        提取每一张图片连接,与标题一并返回,构造生成器
        :param data:
        :return:
        """
        if data.get('data'):
            page_data = data.get('data')
            for item in page_data:
                # cell_type字段不存在的这类文章不爬取,它没有title,和image_list字段,会出错
                if item.get('cell_type') is not None:
                    continue
                title = item.get('title').replace(' |', ' ')    # 去掉某些可能导致文件名错误而不能创建文件的特殊符号,根据具体情况而定
                imgs = item.get('image_list')
                for img in imgs:
                    yield {
                        'title': title,
                        'img': img.get('url')
                    }
    
    
    def save(item):
        """
        根据title创建文件夹,将图片以二进制形式写入,
        图片名称使用其内容的md5值,可以去除重复的图片
        :param item:
        :return:
        """
        img_path = 'img' + '/' + item.get('title')
        if not os.path.exists(img_path):
            os.makedirs(img_path)
        try:
            res = requests.get(item.get('img'))
            if res.status_code == 200:
                file_path = img_path + '/' + '{name}.{suffix}'.format(
                    name=md5(res.content).hexdigest(),
                    suffix='jpg')
                if not os.path.exists(file_path):
                    with open(file_path, 'wb') as f:
                        f.write(res.content)
                    print('Successful')
                else:
                    print('Already Download')
        except requests.ConnectionError:
            print('Failed to save images')
    
    
    def main(offset):
        data = get_data(offset)
        for item in get_img(data):
            print(item)
            save(item)
    
    
    START = 0
    END = 10
    if __name__ == "__main__":
        pool = Pool()
        offsets = ([n * 20 for n in range(START, END + 1)])
        pool.map(main, offsets)
        pool.close()
        pool.join()

      这里定义了起始页START和结束页END,可以自定义设置。然后利用多进程的进程池,调用map()方法实现多进程下载。运行之后发现图片都报存下来了。

                                            

  • 相关阅读:
    算法导论:快速排序
    lintcode:打劫房屋 III
    lintcode:打劫房屋II
    lintcode:打劫房屋
    算法导论:二叉搜索树
    算法导论:整齐打印
    砝码称重问题二
    多重背包问题II
    多重背包问题
    lintcode:背包问题II
  • 原文地址:https://www.cnblogs.com/zivli/p/10952254.html
Copyright © 2011-2022 走看看