【13】Ajax爬取案例实战

目录

一、准备工作

二、爬取目标

三、初步探索:如何判断网页是经js渲染过的?

四、爬取列表页

[4.1 分析Ajax接口逻辑](#4.1 分析Ajax接口逻辑)

[4.2 观察响应的数据](#4.2 观察响应的数据)

[4.3 代码实现](#4.3 代码实现)

(1)导入库

(2)定义一个通用的爬取方法

(3)定义一个爬取列表页的方法

五、爬取详情页

5.1分析详情页

[5.2 如何将详情页与列表页关联起来](#5.2 如何将详情页与列表页关联起来)

[5.3 代码实现](#5.3 代码实现)

六、main总调用

七、完整代码(简化版)


上一节我们学习了 Ajax 的基本原理和分析方法,这一课时我们结合实际案例,学习 Ajax 分析和爬取页面的具体实现。

上期文章: 【12】Ajax的原理和解析-CSDN博客

一、准备工作

安装好 Python 3(最低为 3.6 版本),并能成功运行 Python 3 程序。

了解 Python HTTP 请求库 requests 的基本用法。

了解 Ajax 的基础知识和分析 Ajax 的基本方法。

二、爬取目标

以一个动态渲染网站为例来试验一下 Ajax 的爬取。其链接为:Scrape | Movie,页面如图所示。

这个页面看似和我们上一课时的案例一模一样,但其实不是,它的后台实现逻辑和数据加载方式与上一课时完全不同,只不过最后呈现的样式是一样的!!

本课时我们需要完成的目标有:

分析页面数据的加载逻辑;

用 requests 实现 Ajax 数据的爬取;

将每部电影的数据保存成一个 JSON 数据文件;

三、初步探索:如何判断网页是经js渲染过的?

尝试用之前的requests 来直接提取页面,看看会得到怎样的结果。用最简单的代码实现一下 requests 获取首页源码的过程,代码如下:

python 复制代码
import requests



url = 'https://spa1.scrape.center/page/1'

html = requests.get(url).text

print(html)

运行结果如下:

可以看到我们只爬取到了这么一点 HTML 内容,只是一个空壳,没有数据,也就是说在 HTML 中我们只能在源码中看到引用了一些 JavaScript 和 CSS 文件,并没有观察任何有关电影数据的信息。

如果遇到这样的情况,说明我们现在看到的整个页面是通过 JavaScript 渲染得到的,浏览器执行了 HTML 中所引用的 JavaScript 文件,JavaScript 通过调用一些数据加载和页面渲染的方法,才最终呈现了图中所示的页面。

在一般情况下,这些数据都是通过 Ajax 来加载的, JavaScript 在后台调用这些 Ajax 数据接口,得到数据之后,再把数据进行解析并渲染呈现出来,得到最终的页面。所以说,要想爬取这个页面,我们可以通过直接爬取 Ajax 接口获取数据。

在上一课时中,我们已经了解了用 Ajax 分析的基本方法。下面我们就来分析下 Ajax 接口的逻辑并实现数据爬取吧。

四、爬取列表页

4.1 分析Ajax接口逻辑

首先我们来分析下列表页的 Ajax 接口逻辑,打开浏览器开发者工具,切换到 Network 面板,勾选上 「Preserve Log」并切换到 「XHR」选项卡,重新刷新页面,然后点击第 2 页、第 3 页、第 4 页的按钮,这时候可以看到页面上的数据发生了变化,同时在开发者工具下方会监听到几个 Ajax 请求,如图所示:

由于我们切换了 10页,所以这里正好也出现了 10个 Ajax 请求,我们可以任选一个点击查看其请求详情,观察其请求的 URL、参数以及响应内容是怎样的,如图所示:

这里我们点开第 2 个结果,观察到其 Ajax 接口请求的 URL 地址为:https://spa1.scrape.center/api/movie/?limit=10&offset=10,这里有两个参数,一个是 limit,其值为 10,一个是 offset,它的值也是 10。

通过观察多个 Ajax 接口的参数,我们可以发现这么一个规律:limit 的值一直为 10,这就正好对应着每页 10 条数据;offset 的值在依次变大,页面每加 1 页,offset 就加 10,这就代表着页面的数据偏移量,比如第 2 页的 offset 值为 10 代表跳过 10 条数据,返回从第 11 条数据开始的结果,再加上 limit 的限制,就代表返回第 11~20 条数据的结果。

4.2 观察响应的数据

接着我们再观察下响应的数据,切换到 Preview 选项卡,结果如图所示。

可以看到结果是一些 JSON 数据,它有一个 results 字段,这是一个列表,列表的每一个元素都是一个字典。观察一下字典的内容,发现我们可以看到对应的电影数据的字段了,如 name、alias、cover、categories,对比下浏览器中的真实数据,各个内容是完全一致的,而且这个数据已经非常结构化了,完全就是我们想要爬取的数据,真是得来全不费工夫。、

这样的话,我们只需要把所有页面的 Ajax 接口构造出来,那么所有的列表页数据我们都可以轻松获取到了。

4.3 代码实现

(1)导入库

我们先定义一些准备工作,导入一些所需的库并定义一些配置,代码如下:

python 复制代码
import requests

import logging

logging.basicConfig(level=logging.INFO,

                    format='%(asctime)s - %(levelname)s: %(message)s')

INDEX_URL = 'https://dynamic1.scrape.center/api/movie/?limit={limit}&offset={offset}'

(2)定义一个通用的爬取方法

python 复制代码
def scrape_api(url):

    logging.info('scraping %s...', url)

    try:

        response = requests.get(url)

        if response.status_code == 200:

            return response.json()

        logging.error('get invalid status code %s while scraping %s', response.status_code, url)

    except requests.RequestException:

        logging.error('error occurred while scraping %s', url, exc_info=True)

定义一个 scrape_api 方法,和之前不同的是,这个方法专门用来处理 JSON 接口,最后的 response 调用的是 json 方法,它可以解析响应的内容并将其转化成 JSON 字符串

(3)定义一个爬取列表页的方法

python 复制代码
LIMIT = 10

def scrape_index(page):

    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))

    return scrape_api(url)

定义了一个 scrape_index 方法,用来接收参数 page,page 代表列表页的页码。构造了一个 URL,通过字符串的 format 方法,传入 limit 和 offset 的值。这里的 limit 直接使用了全局变量 LIMIT 的值,offset 则是动态计算的,计算方法是页码数减 1 再乘以 limit,比如第 1 页的 offset 值就是 0,第 2 页的 offset 值就是 10,以此类推。构造好 URL 之后,直接调用 scrape_api 方法并返回结果即可。

这样我们就完成了列表页的爬取,每次请求都会得到一页 10 部的电影数据。

由于这时爬取到的数据已经是 JSON 类型了,所以我们不用像之前一样去解析 HTML 代码来提取数据,爬到的数据就是我们想要的结构化数据,因此解析这一步这里我们就可以直接省略啦。

到此为止,我们就能成功爬取列表页并提取出电影列表信息了。

五、爬取详情页

5.1分析详情页

这时候我们已经可以拿到每一页的电影数据了,但是实际上这些数据还缺少一些我们想要的信息,如剧情简介等,所以我们需要进一步进入到详情页来获取这些内容。

稍加观察我们就可以发现,Ajax 请求的 URL https://spa1.scrape.center/detail/2后面有一个参数是可变的,这个参数就是电影的 id,这里是 2,对应《这个杀手不太冷》这部电影。所以如果我们想要获取 id 为 50 的电影,只需要把 URL 最后的参数改成 50 即可,即 https://spa1.scrape.center/detail/50/,请求这个新的 URL 我们就能获取 id 为 50 的电影所对应的数据了。

同样的,它响应的结果也是结构化的 JSON 数据,字段也非常规整,我们直接爬取即可。

5.2 如何将详情页与列表页关联起来

分析了详情页的数据提取逻辑,那么怎么把它和列表页关联起来呢?这个 id 又是从哪里来呢?我们回过头来再看看列表页的接口返回数据,如图所示:

可以看到列表页原本的返回数据就带了 id 这个字段,所以我们只需要拿列表页结果中的 id 来构造详情页中 Ajax 请求的 URL 就好了。

5.3 代码实现

python 复制代码
DETAIL_URL = 'https://spa1.scrape.center/detail/{id}'



def scrape_detail(id):

    url = DETAIL_URL.format(id=id)

    return scrape_api(url)

这里我们定义了一个 scrape_detail 方法,它接收参数 id。这里的实现也非常简单,先根据定义好的 DETAIL_URL 加上 id,构造一个真实的详情页 Ajax 请求的 URL,然后直接调用 scrape_api 方法传入这个 URL 即可。

六、main总调用

接着,我们定义一个总的调用方法,将以上的方法串联调用起来,代码如下:

python 复制代码
TOTAL_PAGE = 10



def main():

    for page in range(1, TOTAL_PAGE + 1):

        index_data = scrape_index(page)

        for item in index_data.get('results'):

            id = item.get('id')

            detail_data = scrape_detail(id)

            logging.info('detail data %s', detail_data)

这里我们定义了一个 main 方法,首先遍历获取页码 page,然后把 page 当成参数传递给 scrape_index 方法,得到列表页的数据。接着我们遍历所有列表页的结果,获取每部电影的 id,然后把 id 当作参数传递给 scrape_detail 方法,来爬取每部电影的详情数据,赋值为 detail_data,输出即可。

七、完整代码(简化版)

ref: 爬虫基础-Ajax爬取实战_request采集ajkx-CSDN博客

python 复制代码
import requests
import json
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s : %(message)s')

BASE_URL = 'https://spa1.scrape.center/api/movie?limit={limit}&offset={offset}'
INDEX_URl = 'https://spa1.scrape.center/api/movie/{id}/'


'''通用爬取方法'''
def scrape_method(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()
        else:
            logging.error(f"请求{url}的状态码:{requests.status_codes}")
    except requests.RequestException as e:
        logging.error(e)


'''爬取每一页的url'''
def scrape_baseUrl(limitA,offsetA):
    url = BASE_URL.format(limit=limitA,offset=offsetA)
    return scrape_method(url)


'''爬取每一个电影'''
def scrape_indexRul(id):
    url = INDEX_URl.format(id = id)
    return scrape_method(url)


def main():
    limitNum = 10
    # 一共11页offset依次传入0 10 20 .... 100
    for i in range(0,100,10):
        jsonObject = scrape_baseUrl(limitNum,i)
        logging.info(jsonObject.get('results'))
        # 此时results是dict类型,需要转换为json对象,再存入json文件
        for item in jsonObject.get('results'):
            id = item.get('id')
            indexData = scrape_indexRul(id)
            # 以追加的方式将每一部电影的所有属性都存入到bb.json文件中
            with open('bb.json', 'a', encoding='utf-8') as file:
                file.write(json.dumps(indexData, indent=2, ensure_ascii=False))
                file.write('\n')


if __name__ == '__main__':
    main()

运行结果:

json文件:

受不了了。。。自己写的运行不出来。。。

相关推荐
codingandsleeping5 分钟前
Express入门
javascript·后端·node.js
Vaclee8 分钟前
JavaScript-基础语法
开发语言·javascript·ecmascript
拉不动的猪30 分钟前
前端常见数组分析
前端·javascript·面试
小吕学编程1 小时前
ES练习册
java·前端·elasticsearch
Asthenia04121 小时前
Netty编解码器详解与实战
前端
袁煦丞1 小时前
每天省2小时!这个网盘神器让我告别云存储混乱(附内网穿透神操作)
前端·程序员·远程工作
一个专注写代码的程序媛2 小时前
vue组件间通信
前端·javascript·vue.js
一笑code2 小时前
美团社招一面
前端·javascript·vue.js
懒懒是个程序员3 小时前
layui时间范围
前端·javascript·layui
NoneCoder3 小时前
HTML响应式网页设计与跨平台适配
前端·html