linux cpu占用率如何看
284
2022-11-15
动态网页Reptile
文章目录
引入Network什么是 XHR什么是 JSON如何让解析JSON获取多页数据静态、动态网页爬虫总结
引入
我们在使用静态网页爬取方法的时候可能会发生一个问题,有些数据在网页开发者工具中(F12)中存在,而在网页源代码中却没有。
这是因为,例如像电影评论这样的信息是瞬息万变的,可能短短数小时内就多出几百、上千条。如果将每条评论的数据,都通过 HTML 代码的形式写进网页中,那么评论每发生一次变化,就要相应地改变代码,非常繁琐。
那这种实时改变的数据是怎样展现到我们面前的呢?
网页可以通过 API 获取数据,实时更新内容。API 即应用程序接口,它规定了网页与服务器之间可以交互什么数据、通过什么样的方式进行交互。
Network
Network 记录的是从打开浏览器的开发者工具到网页加载完毕之间的所有请求。如果你在网页加载完毕后打开,里面可能就是空的,我们开着开发者工具刷新一下网页即可。
因为一次性加载整个网站很慢,为了提升网页加载速度,有些网站将网站的骨架和内容拆分开,加载骨架后再通过多个请求获取内容,最终组成完整的网站。而有些老的网站或轻量级网站,仍然是一次性返回整个网站的内容,比如豆瓣。
在写爬虫的过程中,我们会经常用到开发者工具中的 Network 面板。接下来我们再来看看 Network 面板 中有哪些常用功能:
④ 展示了一些常用的请求信息,比如请求的名称、状态码、类型、数据大小和耗时等。这些都比较简单,我们只要能看懂,知道是什么意思就行。
在所有请求类型中,有一类非常重要的类型叫做 XHR。
什么是 XHR
上文我们说过,有些网站为了提升网页加载速度,会先加载骨架,再加载详细的内容。而加载详细内容的过程,就用到了 XHR 技术。
XHR 全称 XMLHttpRequest,是浏览器内置的对象。浏览器想要在不刷新网页前提下加载、更新局部内容时,必须通过 XHR 向存放数据的服务器发送请求。
web前端中的AJAX技术就是使用到了XHR
反过来说,XHR 类型请求里,就藏着我们需要的那些不在网页源码中的数据。
接下来我们尝试寻找电影的评论数据。
网站链接:comment(评论),这样我们就能极大地缩小查找范围。
用以上的方法我们很快可以锁定到其中的一个响应:
经过对比我们可以发现就是这条响应中包含着我们要的信息。
id 为 251525 (movieId=251525) ;第 2 页内容 (pageIndex=2);每页 20 条 (pageSize=20)。
遵循某种规则向指定 URL 发送请求,从而获得相应数据的过程,就是 通过 API 获取数据。 下面的 Status Code 是状态码,之前说过,如果是 200,表示成功。其他信息我们无需关注。
另外,为了防止被网站反爬,我们还要在 Headers 中观察一下 Request Headers(请求头),有两个参数要注意。
user-agent 我们之前已经说过,用途是将爬虫伪装成浏览器;另外一个 referer 字段,字面意思是“发起者,发送人”,用来验证这个请求的发起方是否合法。也就是说,服务器要验证这个请求是由谁发出的,只接受从特写网页上发出该请求,比如这里就是该网的网址。如果这个字段不加,可能会爬取失败。
既然找到了获取评论数据的真正链接,以及相关的请求头参数,接下来我们就可以试着通过爬虫来爬取数据了。我们仍然使用 requests.get() 方法获取刚才找到的 API 地址,但 headers 参数要加上 referer 的信息,假装是由时光网自身发起的请求,代码如下:
import requestsheaders = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36', 'referer': '= requests.get('headers=headers)print(res.text)
上面代码中的链接中,comment.api? 后面的内容,是我们前面说过的 查询参数。这次的查询参数内容有些长,直接放在链接里有些乱。好在 requests.get() 方法提供了 params 参数,能让我们以字典的形式传递链接的查询参数,使代码看上去更加的整洁明了。我们看一下官方文档的介绍:
也就是说,链接中的 tt=1641893701852&movieId=251525&pageIndex=2&pageSize=20&orderType=1,可以拆分成一个字典:
params = { "tt": "1641893701852", "movieId": "251525", "pageIndex": "2", "pageSize": "20", "orderType": "1"}res = requests.get( ' params=params, headers=headers)
五个参数中,movieId pageIndex pageSize 的意义我们可以根据字面意思猜到,应该分别代表 电影在该网中的 ID,评论的第x页 和 每页评论数。
电影的页面地址251525 和 movieId 的值一致,可以作为佐证。
那剩下的 orderType 和 tt 是什么?
orderType 字面意思是排序方式,而我们发现,短影评页的右上方的确是有这个选项的。值为 1 代表的应该就是按最热排序。
剩下的 tt 参数,通过经验判断可能是 Unix 时间戳,依据有三:
以 15、16、17 开头,是长整数,与当下时间戳的开头值相符;字母 t 本身也可能和 time(时间)有关;多次尝试刷新网页,重复发起请求,发现 tt 的值每次都在变大,非常像时间的增长。
提示:Unix 时间戳是指格林威治时间 1970 年 01 月 01 日 00 时 00 分 00 秒起至现在的总秒数。获取评论的请求为什么要带上当前的时间戳?具体原因我也不知道,可能是为了验证请求是否过期——如果当前时间戳与服务器时间差距过大,服务器可能认为是非法的过期请求,从而拒绝返回结果(猜想而已)。 有些网站的 API 要求的查询参数非常多,但并不是每个都需要我们去细究并了解原理,只要保证能理解的关键参数都填对就好,其它的参数不懂也没关系,有时可以原封不动照抄进代码中,能返回正确的结果即可。
获取数据的完整代码:
import requestsimport timeheaders = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36', 'referer': '= { # 将当前时间戳转为毫秒后取整,作为 tt 的值 "tt": "{}".format(int(time.time() * 1000)), "movieId": "251525", "pageIndex": "2", "pageSize": "20", "orderType": "1"}res = requests.get( ' params=params, headers=headers)print(res.text)
这样就能看到打印的结果:
{ "code": 0, "data": { "count": 3307, "hasMore": true, "list": [ { "nickname": "瀛26000", "userImage": " "rating": "8.5", "content": "这是我看过的最有血有肉有人情味还紧跟时代潮流的一版哪吒了,没有金吒木吒只有哪吒,李靖没有让人讨厌的琵琶精小老婆,敖丙也不是奸淫掳掠无恶不作的龙二代,申公豹的口吃设计也承包了一部分笑点,太乙真人的火锅味",...省略剩下的内容 },...省略剩下的内容}
所以,从打印结果上看,res.text 是多层级的字典吗?并不是,只是长得像字典的字符串罢了,我们可以验证一下。
print(type(res.text))# 输出:
这种长得像字典的字符串,是一种名为 JSON 的数据格式。我们需要将其转换成真正的 字典/列表,才能从中提取出评论数据
什么是 JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。 易于人阅读和编写,同时也易于机器解析和生成。
JSON 建构于两种结构:键值对的集合 和 值的有序列表,分别对应 Python 里的字典和列表,这些都是常见的数据结构。大部分现代计算机语言都支持 JSON,所以 JSON 是在编程语言之间通用的数据格式。
JSON 本质上就是一个字符串,只是该字符串符合特定的格式要求。也就是说,我们将字典、列表等用字符串的形式写出来就是 JSON,就像下面这样:
# 字典dict = {'price': 233}# JSONjson = '{"price": 233}'# 列表list = ['x', 'y', 'z']# JSONjson = '["x", "y", "z"]'
Tips:Python 字符串使用单引号或双引号没有区别,但 JSON 中,字符串必须使用英文的双引号来包裹。
你可能会有疑问,为什么不直接写成字典或列表,而非要写成 JSON 呢?这是因为不同编程语言的数据结构是不一样的,Python 的字典和列表在别的语言中可能并不写成这样。
而 JSON 是一种标准,规定了基本数据结构的写法,不同的编程语言拿到后解析成自己对应的数据结构即可。JSON 就像普通话,而不同语言的数据结构是方言。普通话是大家都能听懂的,而方言不能。
如何让解析JSON
通过官方文档我们可以知道通过调用 json() 方法即可对响应内容解码。
转换后的结果是 Python 中对应的字典或列表,我们便可根据 Python 的基础知识提取我们需要的数据。不再需要通过 BeautifulSoup 对网页源代码解析,然后再提取数据了。
其实在提取数据的时候也可以不用json去整理数据,可以直接使用正则表达式进行匹配,直接拿到自己想要的信息。
获取多页数据
方法①:通过修改pageindex 代码实现:
import requestsimport timeheaders = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36', 'referer': 'num in range(1, 6): params = { "tt": "{}".format(int(time.time() * 1000)), "movieId": "251525", "pageIndex": "{}".format(num), "pageSize": "20", "orderType": "1" } res = requests.get( ' params=params, headers=headers) comment_list = res.json()['data']['list'] for i in comment_list: print("用户:", i['nickname']) print("评论:", i['content']) # 暂停一下,防止爬取太快被封 time.sleep(1)
方法②:直接修改pagesize即可
import timeimport requestsheaders = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 Edg/101.0.1210.39', 'referer': '= { 'tt': '{}'.format(time.time()), 'movieId': '251525', 'pageIndex': '1', 'pageSize': '100', 'orderType': '1'}rs = requests.get('params = params,headers = headers)order = 0for i in rs.json()['data']['list']: order += 1 print(order,'\n ',i['nickname'],'\n ',i['content'])
静态、动态网页爬虫总结
根据网站是静态的还是动态的,我们爬虫的策略有所不同。静态网页使用 BeautifulSoup 解析网页源代码,动态网页直接找到加载数据的 API,从 API 中爬取数据。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~