10 Ajax数据爬取
https://cuiqingcai.com/202251.html
https://cuiqingcai.com/202252.html
https://cuiqingcai.com/202253.html
10.1 Ajax介绍
Ajax,全称为 Asynchronous JavaScript and XML,即异步的 JavaScript 和 XML。它不是一门编程语言,而是利用 JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。
对于传统的网页,如果想更新其内容,那么必须刷新整个页面,但有了 Ajax,便可以在页面不被全部刷新的情况下更新其内容。在这个过程中,页面实际上是在后台与服务器进行了数据交互,获取到数据之后,再利用 JavaScript 改变网页,这样网页内容就会更新了。
例子: https://m.weibo.cn/u/1874566305
页面其实并没有整个刷新,也就意味着页面的链接没有变化,但是网页中却多了新内容,也就是后面刷出来的新微博。这就是通过 Ajax 获取新数据并呈现的过程。
发送 Ajax 请求到网页更新的这个过程可以简单分为以下 3 步:
- 发送请求
- 解析内容
- 渲染网页
var xmlhttp;
if (window.XMLHttpRequest) {
//code for IE7+, Firefox, Chrome, Opera, Safari
= new XMLHttpRequest();
xmlhttp else {
} //code for IE6, IE5
= new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp
}.onreadystatechange = function () {
xmlhttpif (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
};
}.open("POST", "/ajax/", true);
xmlhttp.send(); xmlhttp
Ajax 其实有其特殊的请求类型,它叫作 xhr
。在图中我们可以发现一个名称以 getIndex
开头的请求,其 Type 为 xhr,这就是一个 Ajax 请求。用鼠标点击这个请求,可以查看这个请求的详细信息。 XHR: XmlHttpRequest — Ajax 向服务器发出的请求,请求数据等。
10.2 Scrape Center 案例
本节我们以一个示例网站来试验一下 Ajax 的爬取,其链接为:https://spa1.scrape.center/,该示例网站的数据请求是通过 Ajax 完成的,页面的内容是通过 JavaScript 渲染出来的,页面如图所示:
10.2.1 初步探索
首先,我们先尝试用之前的 requests 来直接提取页面,看看会得到怎样的结果。用最简单的代码实现一下 requests 获取首页源码的过程,代码如下:
import requests
= 'https://spa1.scrape.center/'
url = requests.get(url).text
html print(html)
如果遇到这样的情况,这说明我们现在看到的整个页面便是 JavaScript 渲染得到的,浏览器执行了 HTML 中所引用的 JavaScript 文件,JavaScript 通过调用一些数据加载和页面渲染方法,才最终呈现了图中所示的结果。
在一般情况下,这些数据都是通过 Ajax 来加载的, JavaScript 在后台调用这些 Ajax 数据接口,得到数据之后,再把数据进行解析并渲染呈现出来,得到最终的页面。所以说,要想爬取这个页面,我们可以直接爬取 Ajax 接口获取数据就好了。
10.2.2 爬取列表页
首先我们来分析一下列表页的 Ajax 接口逻辑,打开浏览器开发者工具,切换到 Network 面板,勾选上 Preserve Log 并切换到 XHR 选项卡,如图所示:
接着重新刷新页面,再点击第二页、第三页、第四页的按钮,这时候可以观察到页面上的数据发生了变化,同时开发者工具下方就监听到了几个 Ajax 请求,如图所示:
由于我们切换了 4 页,每次翻页也出现了对应的 Ajax 请求,我们可以点击查看其请求详情。观察其请求的 URL 和参数以及响应内容是怎样的,如图所示。
观察到其 Ajax 接口请求的 URL 地址为:https://spa1.scrape.center/api/movie/?limit=10&offset=40 这里有两个参数,一个是limit
,这里是 10;一个是 offset
,这里也是 40。
通过多个 Ajax 接口的参数,我们可以观察到这么一个规律:limit
一直为 10,这就正好对应着每页 10 条数据;offset
在依次变大,页面每加 1 页,offset
就加 10,这就代表着页面的数据偏移量,比如第二页的 offset
为 10 则代表着跳过 10 条数据,返回从 11 条数据开始的结果,再加上 limit
的限制,那就是第 11 条至第 20 条数据的结果。
可以看到,结果就是一些 JSON 数据,它有一个 results
字段,是一个列表,列表中每一个元素都是一个字典。观察一下字典的内容,这里我们正好可以看到有对应的电影数据的字段了,如 name
、alias
、cover
、categories
,对比下浏览器中的真实数据,各个内容完全一致,而且这个数据已经非常结构化了,完全就是我们想要爬取的数据,真的是得来全不费工夫。
这样的话,我们只需要把所有页面的 Ajax 接口构造出来,所有列表页的数据我们都可以轻松获取到了。
我们先定义一些准备工作,导入一些所需的库并定义一些配置,代码如下:
import requests
import logging
=logging.INFO,
logging.basicConfig(levelformat='%(asctime)s - %(levelname)s: %(message)s')
= 'https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}' INDEX_URL
这里我们引入了 requests 和 logging 库,并定义了 logging 的基本配置,接着我们定义了 INDEX_URL
,这里把 limit
和 offset
预留出来了变成了占位符,可以动态传入参数构造一个完整的列表页 URL。
下面我们来实现一下详情页的爬取。还是和原来一样,我们先定义一个通用的爬取方法,其代码如下:
def scrape_api(url):
'scraping %s...', url)
logging.info(try:
= requests.get(url)
response if response.status_code == 200:
return response.json()
'get invalid status code %s while scraping %s', response.status_code, url)
logging.error(except requests.RequestException:
'error occurred while scraping %s', url, exc_info=True) logging.error(
这里我们定义了一个 scrape_api
方法,和之前不同的是,这个方法专门用来处理 JSON 接口,最后的 response
调用的是 json
方法,它可以解析响应的内容并将其转化成 JSON 字符串。
接着在这个基础之上,我们定义一个爬取列表页的方法,其代码如下:
= 10
LIMIT
def scrape_index(page):
= INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
url return scrape_api(url)
这里我们定义了一个 scrape_index
方法,它接收一个参数 page
,该参数代表列表页的页码。
这里我们先构造了一个 url
,通过字符串的 format
方法,传入 limit
和 offset
的值。这里 limit
就直接使用了全局变量 LIMIT
的值;offset
则是动态计算的,就是页码数减一再乘以 limit
,比如第一页 offset
就是 0,第二页 offset
就是 10,以此类推。构造好了 url
之后,直接调用 scrape_api
方法并返回结果即可。
这样我们就完成了列表页的爬取,每次请求都会得到一页 10 部的电影数据。
由于这时爬取到的数据已经是 JSON 类型了,所以我们不用像之前那样去解析 HTML 代码来提取数据了,爬到的数据就是我们想要的结构化数据,因此解析这一步就可以直接省略啦。
到此为止,我们能成功爬取列表页并提取出电影列表信息了。
= scrape_index(1)
p_data for 1 in rang(1, 5):
...
10.2.3 爬取详情页
这时候我们已经可以拿到每一页的电影数据了,但是看看这些数据实际上还缺少了一些我们想要的信息,如剧情简介等信息,所以需要进一步进入到详情页来获取这些内容。
这时候点击任意一部电影,如《教父》,进入其详情页,这时可以发现页面的 URL 已经变成了 https://spa1.scrape.center/detail/40,页面也成功展示了详情页的信息,如图所示:
另外,我们也可以观察到在开发者工具中又出现了一个 Ajax 请求,其 URL 为 https://spa1.scrape.center/api/movie/40/,通过 Preview 选项卡也能看到 Ajax 请求对应响应的信息.
稍加观察就可以发现,Ajax 请求的 URL 后面有一个参数是可变的,这个参数就是电影的 id
,这里是 40,对应《教父》这部电影。
如果我们想要获取 id
为 50 的电影,只需要把 URL 最后的参数改成 50 即可,即 https://spa1.scrape.center/api/movie/50/,请求这个新的 URL 我们就能获取 id
为 50 的电影所对应的数据了。
同样,响应结果也是结构化的 JSON 数据,字段也非常规整,我们直接爬取即可。
本节中我们通过一个案例来体会了 Ajax 分析和爬取的基本流程,希望大家通过本节能够更加熟悉 Ajax 的分析和爬取实现。
另外,我们也观察到,由于 Ajax 接口大部分返回的是JSON数据,所以在一定程度上可以避免一些数据提取的工作,这也在一定程度上减轻了工作量。