爬虫代码已复制

Python爬虫实战C07JS逆向实战练习案例解析

打开Python爬虫实战练习C07页面 爬虫实战练习C07,先点击“立即验证”看看要求计算什么数据,发现是要求计算 CPC 列的平均值,目前爬虫通关人数是30人,有点少。

鼠标右键查看一下页面源码,没有发现数据。按照以往的爬虫经验可以得知,这种页面是通过 Ajax 动态加载数据(数据有可能被加密)然后数据解密后渲染到页面上的。

打开浏览器开发者工具,刷新一下页面,看看都加载了什么。结果发现页面暂停了下来,开发者工具源代码/来源标签页面停在了 debugger 这一行:

(function anonymous(
) {
  debugger
})

点击继续运行还是会继续停在这一行。这种无限 debugger 是常见的反代码调试的技术措施,想要绕过也简单,绕过无限调试有两种方法:

  1. 通过替换的方式把 debugger 代码删掉,但如果代码中包含多处 debugger 就比较费时费力;
  2. debugger 本质上属于断点,所以我们可以直接在浏览器开发者工具中停用断点;

停用断点的具体做法是在浏览器开发者工具中切换到源代码\来源选项卡,在最右边的面板上有一个停用断点的图标,默认是灰色的,也就是不启用的状态,点击一下让它启用即可。

停用断点

这个时候重新刷新页面,就会发现页面全部加载完成,没有受 debugger 的影响。

看一下网络,发现有一个 scraper-practice-c07 的 POST 加载了数据。很显示这里就是我们重点分析的地方了。

点击这个请求,在右边展开的面板中切换到启动器,然后点击“请求调用堆栈”,会自动跳转到发起这个请求的代码。此时自动定位到了 c07.min.js 文件的第 429 行,带有 fetch 关键字这一行。

请求参数肯定是在发起请求前生成的,我们从这一行往上看,发现了 md5、key、token、timestamp 这样的关键字,推测这几行就是生成请求参数的关键代码。虽然 js 代码被混淆加密了,但也能大概分析出这几个参数的生成逻辑:

  • key 是从 characters 字符串中随机取32个字符出来;
  • timestamp 是使用 Date.now() 除以 1000 然后取整,就是时间戳精确到秒;
  • token 是从页面元素中取出来的,我们回到页面 html 源码可以找到一个 id="token" 的 input 元素;
  • md5 是 timestamp + token + key 的 MD5 哈希值;

以上4个参数中,有3个出现在了 POST 请求中的载荷(payload)中,说明没有出现的 md5 很有可能是通过其它方式传递的,因为没有发现其它的请求,所以最有可能就是通过 cookie 与请求同时提交的。

回到网络面板,在 POST 中仔细查找一下 cookie 值,发现了类似 _asd2sdf99=c50ff6eb49be1733176af8d714cf2af0 这样的字符串。虽然 cookie 中的值很多,但只有这个字符串最像 MD5,我们把请求中的参数按 timestamp + token + key 的顺序生成一个 MD5 哈希值,就发现上面这个字符串就是我们要找的第4个参数。

请求参数的思路有了,接下来就看看返回的数据,发现从数据API接口返回的数据似乎加密了,CPC 列的值与页面上的对不上,那我们就要断续分析一下 JS 代码了。

刚才的请求参数是在 429 行 fetch 前查找分析,那返回的数据的处理逻辑就要在这之后找了。

在 456 行我们发现了 forEach 这样的关键字,很有可能返回的数据是在这里遍历处理的;在 464 行发现 monthly_search_volume 关键字跟异或运算符,说明 CPC 列的值是跟 monthly_search_volume 列做了异或运算。

我们把返回的数据抽第一组的 CPC 跟 monthly_search_volume 做一个异或运算,得出 5275,而页面上是 52.75,也就是说返回的数据是 (CPC ^ monthly_search_volume) / 100 。可以再抽几组数据验证一下,发现这个逻辑似乎是对的。

这样我们数据API接口返回的结果处理逻辑我们也有了。

现在这里我们就整理一下我们网络爬虫的开发思路:token 是从页面 html 元素中带过来的,而且每次刷新页面都会变,所以这个值很有可能是后台生成传过来的,既然是后台生成的,就很有可能后台是存起来了,我们发送爬虫请求时是需要校验的,所以我们要先请求页面,拿到 token 后再生成相关参数然后向数据API接口发送请求。key 参数是页面上生成的随机字符串,也就是说后台是没有记录的,所以我们可以直接取一下已经发送的值固定它就好了。其它的参数就根据我们刚才分析出来的思路生成就好了。

按照以上的分析结果编写 Python 网络爬虫代码对数据API接口进行爬取,把爬虫返回的结果按要求进行计算,然后验证一下,发现验证通过了。也就是说,我们在这一关的爬虫实战练习当中,靠静态代码分析就完成了整个网络爬虫的开发工作。

静态代码分析、特别是加密混淆的静态代码分析是需要一定的经验,所以大家要多练习,积累经验与感觉。

完整的爬虫案例代码:爬虫案例代码