前言
接触爬虫差不多2年时间,爬过的网站也不少,遇到过的反爬措施无非是 ua 识别、cookie 监测、接口访问频率限制、IP限制这些。直到遇见了 aqistudy —— 让我见识了反爬的新境界。那简直是让你处处惊喜高潮迭起欲罢不能,说来以前真的是井底之蛙孤陋寡闻了。
这个网站呢,看B站2019年录制的视频,当时还是挺“正常”的1个网站,没有现在这些“花里胡哨”的东西。我猜是不是这4年里被人爬得太狠了,才弄了这么多复杂甚至“变态”的反爬措施。网站上面的天气数据虽然还在每天更新,但是https证书有问题,浏览器会提示你连接不安全,不知道是不是网站管理员快要放弃维护了。
爬虫和反爬虫,最坚固的盾和最锋利的矛,永远是互相依存互相激发互相促进的。也正是得益于这些反爬措施,让我从年前到现在折腾近半个月,每一处障碍都转化成了惊喜。惊的是新发现,喜的是新认知,新收获。所有那些耗费时间和心力之后的突破,无一不转化成了对网站开发人员如滔滔江水绵延不绝发自内心的敬意。
还是那句话,写下这些,更多是记录。如果能够得上分享,也请大家把关注点更多放在技术层面的提升,而不是无节制地爬取和伤害。记录和分享,永远是一件有意义的事情。
正文
把从开始接触,到最终拿到数据,分了大概3个阶段。给每个阶段硬生生起了歪名,权当锻炼脑筋吧。
第一阶段:初来乍到 小试牛刀
- 右键被禁用,无法查看网页源代码 。可通过在地址栏输入 view-source:网址 解决。
- F12被禁用,无法打开开发者工具。可通过 Ctrl + Shift + i 或者 菜单入口进入
- 无限Debugger,非法调试。可通过调整开发者工具窗口为单独窗口,设置 Script 事件断点,然后再访问网址解决
第二阶段:长风破浪 峥嵘终现
- 前端代码编码。需要找出随时间戳变化的字符串参数,结合固定的 jquery 解码代码,执行解码
- ajax 请求数据 payload 编码,需要从经过解码后的 js 代码中找到 key,另外调用解码后的 js 代码中的编码函数得到 value
- 接口响应数据编码,需要找出接口名称发出请求,然后调用解码后的 js 代码对 response 进行进行解码
第三阶段:返璞归真 九重飞升
- 第二阶段属于万军丛中直捣黄龙,已经可以调用接口直接拿到原始数据了,这里第三阶段其实是回到起点——B站视频的提供的思路,通过Selenium获取渲染后包含数据的页面源代码
- 前端数据鱼龙混杂。页面上只有1个表格,但是代码中提供了3个 Table,每个Table内都有数据。3个Table 的 style 存在差异,可以通过正则表达式匹配后,将其中2个替换为空。
- css 样式以假乱真,即便是包含真实数据的 Table。其中也夹杂了包含假数据的 td,如图。但是这些假数据是通过 hidden-lg, hidden, display:none 以及 display:none 对应的别名字符串这4种样式实现了隐藏,虽然在页面中不会看到,但是会影响从源代码中提取。需要通过正则表达式,分别匹配这4种样式,然后替换
- 表头字段不仅会随着时间变化而变化排列顺序,表头中也包含了假数据。和上面一样,可以通过正则表达式匹配样式剔除包含假数据的 td 。
- 表头字段有时候会在 td 标签中,有时候会在 th 标签中。以 阿克苏地区,2015年1月为例,13:01:18(下图左) 和 13:01:58(下图右) 分别获取2次网页源代码(已经去掉了2个伪数据 table),左图中表头在 td 标签,右图中表头在 th 标签中 —— 右图中明显看到重复的字段,只有1个是真实展示的。这种情况也是需要正则匹配隐藏样式,将无用干扰项剔除。
- 如果经历过第二阶段,你会发现上面这些以假乱真的效果,都是通过js 代码实现的。如下图。图中只有第1个 Table 的数据是从接口返回中提取,剩下2个都是通过 Math.random() 计算得到。而且第1个 Table 中间也夹杂了 Math.random(),和上面我们观察到的前端效果一致。
- webdriver 被限制。如果直接使用 Selenium,拿到的也是不包含渲染数据的页面源代码,因为网站上通过识别 windows.navigator 属性字段来判断是否要对网页进行渲染。这里可以通过让 webdriver 执行 js 代码以避开监测
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
})
- 无头模式 被限制。从渲染后的网页源码中提取数据,这个逻辑本身就已经效率低下了,使用Selenium 时如果再调用浏览器打开可见窗口,效率就更低了,使用 Selenium 的 --headless 模式可以有效缓解这个问题。但是和上面一样,网站上也有对无痕模式的监测,如果监测到,也就不再对网页进行渲染。所以也是要通过webdriver 执行 js 代码来设置 plugins 属性以避开监测
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5],
})
"""
})
- 同样,如果经历过第二阶段,你会发现上面这些对Selenium的监测和限制,也是通过js 代码实现的
结语
不确定网站有没有针对IP地址、或者接口调用做频率限制,鉴于我目前一直都是非常审慎地操作,所以只遇到过极个别的 403状态,大部分情况都能拿到数据。后面如果有新的发现,我会酌情继续补充。
就到这里吧,整理材料简直比探究逻辑代码实现还要费心神。@202301122339