[Python3爬虫]Ajax请求信息的爬取

动态加载页面信息的提取

当我们浏览一个新闻类的网站,例如微博,今日头条,知乎等,由于它的内容极多,当我们搜索某一关键词的信息后,服务器只会向我们返回少量的数据,微博和头条是返回指定数量的数据,当我们再次向下刷新的时候,会再次通过Ajax请求返回指定数目的数据(如果你的网络不好时,会出现一个表示正在加载的小圆圈的动画效果)。知乎是当浏览器的滚动条触底时,再次提取数据。这就产生了一个问题,通过爬虫如何来提取通过Ajax请求动态加载的数据呢?

模拟Ajax请求

这时需要通过Chrome等浏览器的开发者工具,利用Chrome开发者工具的筛选功能筛选出所有的Ajax请求。选择network选项,直接点击XHR分析网页后台向接口发送的Ajax请求,用requests来模拟Ajax请求,那么就可以成功抓取信息了

待爬取网站的Ajax请求的分析

这里选择今日头条来搞事情,通过爬虫来下载头条上街拍关键词的图片,搜索关键词街拍,分析Ajax请求。下面是提取到的Ajax的主要信息

Request URL: https://www.toutiao.com/search_content/?
offset=0&
format=json&
keyword=%E8%A1%97%E6%8B%8D&
autoload=true&
count=20&
cur_tab=1&
from=search_tab
1.分析请求

https://www.toutiao.com/search_content/ 这个是请求的链接,后面还带了一个 ? 号,之后的都是请求所带的参数

pic1.PNG

通过刷新新内容不断的发送Ajax请求,对比不同的几个ajax请求,对比他们的不变的地方和改变的地方,为写程序做好准备。

  • 可以发现每加载一次内容参数offset加20,表示偏移量,每次取20条数据

  • format是不变的,表示格式是json格式的,

  • Keyword是我们搜索的关键字
    %E8%A1%97%E6%8B%8D& ,,可能是中文的某种加密方式加密后的结果

  • 发现offset加20就可以了,其他参数照搬,因为都是不变的参数

2.分析响应
pic2.PNG

点击Preview分析响应内容。我们要下载图片,发现图片链接都在image_list里,一篇文章的一张或多张图片都在里面,而外层是data属性,标题在title属性里,这里获取标题名作为文件夹名称进行存储

代码实现

  • 首先,实现方法get_page()来加载单个Ajax请求的结果。其中唯一变化的参数就是offset,所以我们将它当作参数传递,代码实现如下:
import requests
from urllib.parse import urlencode  #Python内置的HTTP请求库

def get_page(offset):
    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)  #拼接URL
    try:
        r = requests.get(url)
        if r.status_code == 200:
            return r.json()  # 返回json格式的响应内容
    except:
        return None
  • urllib库

Python内置的HTTP请求库,通常我们使用的是功能更为强大的requests库,用到urllib的parse工具模块,提供了许多URL处理方法,比如拆分、解析、合并等。用urlencode()方法构造请求的GET参数。

  • 接下来,再实现一个解析方法:提取每条数据的image_list字段中的每一张图片链接,将图片链接和图片所属的标题一并返回,同时构造一个生成器。实现代码如下:
def get_images(jsondata):
if jsondata.get('data'):
    for item in jsondata.get('data'):
        title = item.get('title')
        images = item.get('image_list')
        for image in images:
            yield {
                'image' : image.get('url'),
                'title' : title
            }
  • 接下来,实现一个保存图片的方法save_image(),其中item就是前面get_images()方法返回的一个字典。在该方法中,首先根据item的title来创建文件夹,然后请求这个图片链接,获取图片的二进制数据,以二进制的形式写入文件。图片的名称可以使用其内容的MD5值,这样可以去除重复。
def save_image(item):
if not os.path.exists(item.get('title')):
    os.mkdir(item.get('title'))
try:
    image_url = item.get('image')
    print(image_url)
    r = requests.get('http:'+image_url)
    if r.status_code == 200:
        file_path = '{0}/{1}.{2}'.format(item.get('title'),md5(r.content).hexdigest(),'jpg')
        if not os.path.exists(file_path):
            with open(file_path,'wb') as f:
                f.write(r.content)
        else:
            print('Already Downloaded', file_path)
except:
    print('Faild to Save Image')

最后,构造一个offset数组,遍历offset,提取图片链接,并将其下载:

def main(offset):
jsondata = get_page(offset)
for item in get_images(jsondata):
    print(item)
    save_image(item)

num_start = 1
num_end = 20

if __name__ == '__main__':
    pool = Pool()
    num = ([x * 20 for x in range(num_start,num_end + 1)])
    pool.map(main,num)
    pool.close()
    pool.join()
  • 整体代码
import requests
from urllib.parse import urlencode
import os
from hashlib import md5
from multiprocessing.pool import Pool

def get_page(offset):
    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:
        r = requests.get(url)
        if r.status_code == 200:
            return r.json()
    except:
        return None

def get_images(jsondata):
    if jsondata.get('data'):
        for item in jsondata.get('data'):
            title = item.get('title')
            images = item.get('image_list')
            for image in images:
                yield {
                    'image' : image.get('url'),
                    'title' : title
                }

def save_image(item):
    if not os.path.exists(item.get('title')):
        os.mkdir(item.get('title'))
    try:
        image_url = item.get('image')

        r = requests.get('http:'+ image_url)
        if r.status_code == 200:
            file_path = '{0}/{1}.{2}'.format(item.get('title'),md5(r.content).hexdigest(),'jpg')
            if not os.path.exists(file_path):
                with open(file_path,'wb') as f:
                    f.write(r.content)
            else:
                print('Already Downloaded', file_path)
    except:
        print('Faild to Save Image')

def main(offset):
    jsondata = get_page(offset)
    for item in get_images(jsondata):
        print(item)
        save_image(item)

num_start = 1
num_end = 20
    
    
if __name__ == '__main__':
    pool = Pool()
    num = ([x * 20 for x in range(num_start,num_end + 1)])
    pool.map(main,num)
    pool.close()
    pool.join()

这里定义了分页的起始页数和终止页数,分别为num_start和num_end,还利用了多线程的线程池,调用其map()方法实现多线程下载

运行效果

pic3.PNG

关于作者

个人博客:https://Yhchdev.github.io

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,924评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,776评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,954评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,576评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,589评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,105评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,480评论 3 427
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,457评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,998评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,026评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,174评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,812评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,502评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,979评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,101评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,674评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,204评论 2 363

推荐阅读更多精彩内容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,538评论 1 45
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,395评论 25 707
  • 今天看到一句话,特别应景,激起了心底的些许涟漪。 “夜深人静,独坐观心,使之妄穷而真独露,每于此中得大机趣;既觉真...
    沐子老妖阅读 1,126评论 0 1
  • 看到只是别人的人,总是心里感到非常的不舒服。违反了自己关于人性啊,道德啊,平等啊之类的认识。 不太喜欢那种太以自我...
    热情下午阅读 297评论 0 0
  • 夜间出行的动物有很多,人是比较明目张胆的一种。 凌晨在酒吧或是烧烤档喝酒猜拳的人,搁在几年前...
    南苝阅读 174评论 0 0