《PYTHON3网络爬虫开发实践》——第六章 Ajax数据爬取

第六章 Ajax数据爬取

  • 有时候我们在用requests抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:这是因为requests 获取的都是原始的HTML文档,而浏览器中的页面则是经过JavaScript处理数据后生成的结果,这些数据的来源有多种,可能是通过Ajax加载的,可能是包含在HTML文档中的,也可能是经过JavaScript和特定算法计算后生成的。
    对于第一种情况,数据加载是一种异步加载方式,原始的页面最初不会包含某些数据,原始页面加载完后,会再向服务器请求某个接口获取数据,然后数据才被处理从而呈现到网页上,这其实就是发送了一个Ajax请求。

  • Ajax

    Ajax,全称为Asynchronous JavaScript and XML,即异步的JavaScript 和XML。它不是一门编程语言,而是利用JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。

    比如微博的“加载”按钮。页面其实并没有整个刷新,也就意味着页面的链接没有变化,但是网页中却多了新内容,也就是后面刷出来的新微博。这就是通过Ajax获取新数据并呈现的过程。

  • Ajax请求到网页更新的这个过程可以简单分为3步:

    • 发送请求

      image-20190225113628014

      这是JavaScript 对Ajax 最底层的实现,实际上就是新建了XMLHttpRequest 对象,然后调用onreadystatechange属性设置了监听,然后调用open()和send()方法向某个链接(也就是服务器)发送了请求。前面用Python实现请求发送之后,可以得到响应结果,但这里请求的发送变成JavaScript来完成。由于设置了监听,所以当服务器返回响应时,onreadystatechange 对应的方法便会被触发,然后在这个方法里面解析响应内容即可。

    • 解析内容

      得到响应之后,onreadystatechange属性对应的方法便会被触发,此时利用xmlhttp 的responseText属性便可取到响应内容。这类似于Python中利用requests向服务器发起请求,然后得到响应的过程。那么返回内容可能是HTML,可能是JSON,接下来只需要在方法中用JavaScript进一步处理即可。比如,如果是JSON的话,可以进行解析和转化。

    • 渲染网页

      JavaScript有改变网页内容的能力,解析完响应内容之后,就可以调用JavaScript来针对解析完的内容对网页进行下一步处理了。比如,通过document . getElementById(). innerHTML这样的操作,便可以对某个元素内的源代码进行更改,这样网页显示的内容就改变了,这样的操作也被称作DOM操作,即对Document网页文档进行操作,如更改、删除等。
      上例中,document . getElementById(“myDiv” ). innerHTML=xmlhttp. responseText 便将ID为myDiv的节点内部的HTML代码更改为服务器返回的内容,这样myDiv元素内部便会呈现出服务器返回的新数据,网页的部分内容看上去就更新了。
      这3个步骤其实都是由JavaScript 完成的,它完成了整个请求、解析和渲染的过程。再回想微博的下拉刷新,这其实就是JavaScript向服务器发送了一个Ajax请求,然后获取新的微博数据,将其解析,并将其渲染在网页中。

  • 以微博为例走一下Ajax分析方法

    Ajax有特殊的请求类型,叫xhr,这种类型的文件就是Ajax请求。

    Request Headers中有一个信息为X-Requested-With:XMLHttpRequest。

    preview返回的是个人信息如昵称、简介、头像等,以JSON格式存储。

    过滤请求

    Network——XHR,此时下方显示的所有请求便都是Ajax请求了。

    接下来,不断滑动页面,可以看到页面底部有一条条新的微博被刷出,而开发者工具下方也一个个地出现Ajax请求,这样我们就可以捕获到所有的Ajax请求了。
    随意点开一个条目,都可以清楚地看到其Request URL、Request Headers、 Response Headers、Response Body等内容,此时想要模拟请求和提取就非常简单了。

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

    import requests
    from urllib.parse import urlencode
    from requests import codes
    import os
    from hashlib import md5
    from multiprocessing.pool import Pool
    import re
    '''
    https://github.com/Python3WebSpider/Jiepai
    '''
    
    
    def get_page(offset):
        params = {
            'aid': '24',
            'offset': offset,
            'format': 'json',
            #'keyword': '街拍',
            'autoload': 'true',
            'count': '20',
            'cur_tab': '1',
            'from': 'search_tab',
            'pd': 'synthesis'
        }
        base_url = 'https://www.toutiao.com/api/search/content/?keyword=%E8%A1%97%E6%8B%8D'
        url = base_url + urlencode(params)
        try:
            resp = requests.get(url)
            print(url)
            if 200  == resp.status_code:
                print(resp.json())
                return resp.json()
        except requests.ConnectionError:
            return None
    
    
    def get_images(json):
        if json.get('data'):
            data = json.get('data')
            for item in data:
                if item.get('cell_type') is not None:
                    continue
                title = item.get('title')
                images = item.get('image_list')
                for image in images:
                    origin_image = re.sub("list", "origin", image.get('url'))
                    yield {
                        'image':  origin_image,
                        # 'iamge': image.get('url'),
                        'title': title
                    }
    
    print('succ')
    
    def save_image(item):
        img_path = 'img' + os.path.sep + item.get('title')
        print('succ2')
        if not os.path.exists(img_path):
            os.makedirs(img_path)
        try:
            resp = requests.get(item.get('image'))
            if codes.ok == resp.status_code:
                file_path = img_path + os.path.sep + '{file_name}.{file_suffix}'.format(
                    file_name=md5(resp.content).hexdigest(),
                    file_suffix='jpg')
                if not os.path.exists(file_path):
                    print('succ3')
                    with open(file_path, 'wb') as f:
                        f.write(resp.content)
                    print('Downloaded image path is %s' % file_path)
                    print('succ4')
                else:
                    print('Already Downloaded', file_path)
        except requests.ConnectionError:
            print('Failed to Save Image,item %s' % item)
    
    
    def main(offset):
        json = get_page(offset)
        for item in get_images(json):
            print(item)
            save_image(item)
    
    
    GROUP_START = 0
    GROUP_END = 7
    
    if __name__ == '__main__':
        pool = Pool()
        groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)])
        pool.map(main, groups)
        pool.close()
        pool.join()
    

猜你喜欢

转载自blog.csdn.net/qq_39362996/article/details/88029341
今日推荐