当前位置:网站首页>人均瑞数系列,瑞数 4 代 JS 逆向分析

人均瑞数系列,瑞数 4 代 JS 逆向分析

2022-07-06 15:42:00 VIP_CQCRE

这是「进击的Coder」的第 656 篇技术分享

作者:K 小哥

来源:K 哥爬虫

阅读本文大概需要 13 分钟。

c512798e4b455fb16bcff4b9ff1e3355.png

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!

前言

be538ef852fa5ef330d411b33bbae37d.png

瑞数动态安全 Botgate(机器人防火墙)以“动态安全”技术为核心,通过动态封装、动态验证、动态混淆、动态令牌等技术对服务器网页底层代码持续动态变换,增加服务器行为的“不可预测性”,实现了从用户端到服务器端的全方位“主动防护”,为各类 Web、HTML5 提供强大的安全保护。

瑞数 Botgate 多用于政企、金融、运营商行业,曾一度被视为反爬天花板,随着近年来逆向大佬越来越多,相关的逆向文章也层出不穷,真正到了人均瑞数的时代了,这里也感谢诸如 Nanda、懒神等逆向大佬,揭开了瑞数神秘的面纱,总结的经验让后来人少走了不少弯路。

过瑞数的方法基本上有以下几种:自动化工具(要隐藏特征值)、RPC 远程调用、JS 逆向(硬扣代码和补环境),本文介绍的是 JS 逆向硬扣代码,尽可能多的介绍各种细节。

瑞数特征以及不同版本的区别

对于绝大多数使用了瑞数的网站来说,有以下几点特征(可能有特殊版本不一样,先仅看主流的):

1、打开开发者工具(F12)会依次出现两个典型的无限 debugger:

ff4f4f8ab9adc7db712a68f71c51cca9.png

82af0169a7bac36a4df5610080b8ab67.png

2、瑞数的 JS 混淆代码中,变量、方法名大多类似于 _$xx,有众多的 if-else 控制流,新版瑞数还可能会有 jsvmp 以及众多三目表达式的情况:

9a4402a39e90c7c518a073933ada7e81.png


3、看请求,会有典型的三次请求,首次请求响应码是 202(瑞数3、4代)或者 412(瑞数5代),接着单独请求一个 JS 文件,然后再重新请求页面,后续的其他 XHR 请求中,都带有一个后缀,这个后缀的值是由 JS 生成的,每次都会变化,后缀的值第一个数字为瑞数的版本,比如 MmEwMD=4xxxxx 就是4代瑞数,bX3Xf9nD=5xxxxx 就是5代瑞数:

6e46bccd41ba1bac11c1a37ae54ee0c7.png

c72d9ce3b1aa5b15afd287e2afcc2e9a.png

a91d71cb1565000cd80f6ed8038e43d3.png

855b696b0c7b396a26b50c2bb357424f.png

4、看 Cookie,瑞数 3、4 代有以 T 和 S 结尾的两个 Cookie,其中以 S 开头的 Cookie 是第一次的 201 那个请求返回的,以 T 开头的 Cookie 是由 JS 生成的,动态变化的,T 和 S 前面一般会跟 80 或 443 的数字,Cookie 值第一个数字为瑞数的版本(为什么可以通过第一个数字来判断版本?难道相同版本第一个数字不会变吗?这些问题我们在分析 JS 的时候可以找到答案),比如:

  • FSSBBIl1UgzbN7N80T=37Na97B.nWX3....:数字 80 是 http 协议的默认端口号,对应 http 请求,其值第一位为 3,表示 3 代瑞数;

  • FSSBBIl1UgzbN7N443T=4a.tr1kEXk.....:数字 443 是 https 协议的默认端口号,对应 https 请求,其值第一位为 4,表示 4 代瑞数。

aa478e47254cbddea4fbca33f6cc7783.png

瑞数 5 代也有以 T 和 S 结尾的两个 Cookie,但有些特殊的 5 代瑞数也有以 O 和 P 结尾的,同样的,以 O 开头的是第一次的 412 那个请求返回的,以 P 开头的是由 JS 生成的,Cookie 值第一个数字同样为瑞数的版本,和 3、4 代不同的是,5 代没有加端口号了,比如:

  • vsKWUwn3HsfIO=57C6DwDUXS.....:以 O 结尾,其值第一位为 5,表示 5 代瑞数;

  • WvY7XhIMu0fGT=53.9fybty......:以 T 结尾,其值第一位为 5,表示 5 代瑞数。

5c264afd75bc73a6b3bdc904619bd8ac.png

e3a47d522888339694d6c3812c7ebc61.png

5、看入口,瑞数有个流程是在虚拟机 VM 中加载 1w+ 行的代码,加载此代码的入口,不同版本也不一样(这个入口具体在哪里?怎么定位?在后续逆向分析中再详细介绍),示例如下:

  • 3 代:_$aW = _$c6[_$l6()](_$wc, _$mo);_$c6 实际上是 eval_$l6() 实际上是 call

2f4418cd30cb11ebbe9f4aacdf8e35fd.png

  • 4 代:ret = _$DG.call(_$6a, _$YK);_$DG 实际上是 eval,有关键字 retcall 是明文;

0103f69c217672cf7e7d9b9151126b0c.png

  • 5 代:5 代种类比较多了,最初和 4 代的类似,比如 ret = _$Yg.call(_$kc, _$mH);,有关键字 ret,call 是明文,也有没有 ret 关键字的版本,比如 _$ap = _$j5.call(_$_T, _$gp);,也有像 3 代那样全部混淆了的,比如:_$x8 = _$mP[_$nU[15]](_$z3, _$Ec);_$mP 实际上是 eval_$nU[15] 实际上是 call,混淆的 call 与 3 代的区别就是 5 代是在一个数组里取值得到的;

07aba42918388facaedb1d94e9798150.png

47b51bc9b28266aafd48eee82b20d284.png

940b6e9d39f8f21314c1b8af157c4a4c.png


当然要想精准区分不同版本,得各个条件结合起来看,最主要的还是得看看内部的实现逻辑,以及页面的代码结构,比如 4 代有一个生成假 Cookie 的步骤,而 5 代没有,有的特殊版本虽然看起来是 5 代,但是加了 jsvmp 和三目表达式,和传统的 5 代又有区别,偶尔愚人节啥的突然来个新版本,也会不一样,各版本在分析一遍之后,就很容易区分了。

Cookie 入口定位

本文案例中瑞数 4 代网站为:aHR0cDovL3d3dy5mYW5nZGkuY29tLmNuL25ld19ob3VzZS9uZXdfaG91c2VfZGV0YWlsLmh0bWw=

首先过掉无限 debugger(过不过其实无所谓,后面的分析其实这个基本上没影响),直接右键 Never pause here 永不在此处断下即可:

5e1a0ec667d25e5d9103ae6dfd99012d.png


定位 Cookie,首选 Hook 来的最快,通过 Fiddler 等抓包工具、油猴脚本、浏览器插件等方式注入以下 Hook 代码:

(function() {
    // 严谨模式 检查所有错误
    'use strict';
    // document 为要hook的对象 这里是hook的cookie
 var cookieTemp = "";
    Object.defineProperty(document, 'cookie', {
  // hook set方法也就是赋值的方法 
  set: function(val) {
    // 这样就可以快速给下面这个代码行下断点
    // 从而快速定位设置cookie的代码
    console.log('Hook捕获到cookie设置->', val);
                debugger;
    cookieTemp = val;
    return val;
  },
  // hook get 方法也就是取值的方法 
  get: function()
  {
   return cookieTemp;
  }
    });
})();

Hook 发现会有生成两次 Cookie 的情况,断下之后往上跟栈,可以看到组装 Cookie 的代码,类似如下结构:

7fc2f52df35b3253adb265d7930272eb.png


仔细观察这两次 Cookie 生成的地方,分别往上跟栈,你就会发现两个 Cookie 分别是经过了两个不同方法得到的,如下图所示:

9461910ac7355352aa4443f38c2231f7.png

a17fb26a411650efc9cd732ab6bdf737.png


这里的代码存在于 VM 虚拟机中,且是 IIFE 自执行代码,我们还得往前跟栈看看这些 VM 代码是从哪里加载出来的,跟栈来到首页(202页面)带有 call 的位置:

3c7559cda44533af7504c3b395b5e70a.png

我们在文章开头介绍的这个位置就是这么分析得来的,这个位置通常在分析瑞数的时候作为入口,图中 _$te 实际上是 eval 方法,传入的第一个参数 _$fY 是 Window 对象,第二个对象 _$F8 是我们前面看到的 VM 虚拟机中的 IIFE 自执行代码。

在知道了瑞数大致的入口之后,我们也可以使用事件监听中的 Script 断点,一直下一个断点(F8)就可以走到 202 页面,然后搜索 call 关键字就能快速定位到入口,Script 断点中的两个选项,第一个表示运行 JS 脚本的第一条语句时断下,第二个表示 JS 因为内容安全政策而被屏蔽时断下,一般选择第一个就可以了,如下图所示:

b0b8fc86992ae6479db300149d0a03b7.png

文件结构与逻辑

想要后续分析 Cookie 的生成,我们不得不要观察一下 202 页面的代码,meta 标签有个 content 内容,引用了一个类似于 c.FxJzG50F.dfe1675.js 的 JS 文件,接着跟一个自执行的 JS,如下图所示:

390248bb850f63a62f63f436b2d04c2d.png


第1部分 meta 标签的 content 内容,每次都是变化的,第2部分引用的这个外部 JS 在不同页面也有所差别,但是同一个网站同一个页面 JS 里的内容一般是固定不会变的,第3部分自执行代码每次变化的只是变量名,整体逻辑不变,后续我们在扣代码的时候,也会用到这里的部分方法。自执行代码里同样也是有很多 if-else 控制流,开头的那个数组,比如上图中的 _$Dk 就是用来控制后续的控制流的。

引用的 c.FxJzG50F.dfe1675.js 直接打开看是乱码的,而自执行 JS 的主要作用是将这 JS 乱码还原成 VM 里的 1w+ 行的正常代码,并且定义了一个全局变量 window.$_ts 并赋了许多值,这个变量在后续 VM 中作用非常大,meta 标签的 content 内容同样也会在 VM 里用到。

由于很多值、变量都是动态变化的,肯定不利于我们的分析,所以我们需要固定一套代码到本地,打断点、跟栈都会更加方便,随便保存一份 202 页面的代码,以及该页面对应的外链 JS 文件,如 c.FxJzG50F.dfe1675.js 到本地,使用浏览器自带的 overrides 重写功能、或者浏览器插件 ReRes、或者抓包工具的响应替换功能(如 Fiddler 的 AutoResponder)进行替换。

c6d1c2b16a62510868277466c072bea4.png

VM 里面的代码是生成 Cookie 的主要代码,包含众多的 if-else 控制流,无疑增加了我们分析代码的成本,这里就可以使用 AST 技术做一下反混淆,比如 Nanda 就将 if-else 控制流转换成了 switch-case 的,同一个控制流下的代码放在了同一个 case 下,然后在 call 入口那个地方,将 VM 代码做一下本地替换,具体可以参考 Nanda 的文章:《某数4代逻辑分析》,感兴趣的可以试试,不了解 AST 的可以看看以前的文章《逆向进阶,利用 AST 技术还原 JavaScript 混淆代码》,后续有时间 K 哥再写写 AST 还原瑞数代码的实战,本文咱们选择硬刚!

16013e7c3f8ececdbad90eaf7d187170.jpeg

VM 代码以及 $_ts 变量获取

前面我们了解了 VM 代码和 $_ts 的重要性,所以我们第一步是要想办法拿到他们,至于在什么时候有用到,文章后续再说,复制外链 JS,即  c.FxJzG50F.dfe1675.js 的代码和 202 页面的自执行代码到文件,本地直接运行即可,需要轻度补一下环境,缺啥补啥,大致补一下 window、location、document 就行了,补的具体内容可以直接在浏览器控制台使用 copy() 命令复制过来,然后 VM 代码我们就可以直接 Hook eval 的方式得到,大致的补环境代码如下:

var eval_js = ""

window = {
    $_ts:{},
    eval:function (data) {
        eval_js = data
    }
}

location = {
    "ancestorOrigins": {},
    "href": "http://www.脱敏处理.com.cn/new_house/new_house_detail.html",
    "origin": "http://www.脱敏处理.com.cn",
    "protocol": "http:",
    "host": "www.脱敏处理.com.cn",
    "hostname": "www.脱敏处理.com.cn",
    "port": "",
    "pathname": "/new_house/new_house_detail.html",
    "search": "",
    "hash": ""
}

document = {
    "scripts": ["script", "script"]
}

d11195087d05929690dc264af11edaaf.png


观察 $_ts 的 key 和 value,和浏览器中得到的是一样的:

7dd30e1fdbb594a07c1faddc9e18a305.png


注意事项:c.FxJzG50F.dfe1675.js 外链 JS 如果你直接下载下来用编辑器打开可能会被自动编码,和原始数据有出入,导致运行报错,这里建议直接在浏览器在线访问这个文件,手动复制过来,或者在抓包软件里将响应内容复制过来,观察以下两种情况,第一种情况就可能会导致运行出错,第二种是正常的:

3e89a6255b1d1935855ad59b03f12440.png


扣代码

前面说了这么多,现在终于可以进入主题了,那就是扣代码,找个好椅子,准备把屁股坐穿,此时你的键盘只有 F11 有用,不断单步调试,只需要亿点点细节,就完事儿了!

扣代码步骤太多,不可能每一步都截图写出来,只写一下比较重要的,如有遗漏的地方,那也没办法7492a14d75b2b110f5e735571799f498.png,首先先在我们替换的 202 页面里,自执行代码开始的地方手动加个 debugger,一进入页面就断下,方便后续的分析:

b05b3336ed988e5d282055cd6de18795.png


通过前面我们的分析,已经知道了入口在 call 的地方,快速搜索并下断点:

4bb96e91f901d679979e4477ffbf68d1.png

通过前面我们的分析,我们也知道了有两次生成 Cookie 的地方,快速搜索 (5),搜索结果第二个即为入口:

5f4662a871977e877a7791664f08c35b.png

假 Cookie 生成逻辑

首先单步跟假 Cookie,虽然是假的,但是后续生成真 Cookie 中会用到,在跟的时候你会走到这个逻辑里面:

84ffee36c0458073189731f69bacdf0d.png

有一步会调用 _$8e() 方法,而 _$8e = _$Q9_$Q9 又嵌套在 _$d0 里的,搜索一下哪里调用了 _$d0,发现是代码开头:

dcd405f5e75af689d5363f2ae53f81b1.png

那么传入的参数 _$Wn 是啥呢?单步跟入,是一个方法,作用就是取 202 页面的 content 内容,那么我们在本地就直接删掉这个 _$Wn 方法,直接传入 content 的值即可,如下图所示:

ead32c0d3eac3f9ffc266cd48ef67c4b.png


另外,我们发现,代码有非常多的在数组里面按索引取值的情况,比如上图中的 _$PV[68] 的值,实际上就是字符串 content,很显然我们要把这个数组的来源找到,直接搜索 _$PV =,可以找到疑似定义和赋值的地方:

19a159955cbf38d78ef4e73d9a38bef6.png

3dc93944278b2d3c75e112b735980581.png


所以我们得看看这个 _$iL 方法,传入了一个非常长的字符串,打断点进去看看,果然生成了 _$PV,是一个 725 位的数组:

47fcce92fc57334cf45f8ad3b16f131f.png

接下来在扣代码的过程中,你会经常遇到一个变量,在本文中是 _$sX

43648e589d6bb22d6cb35e00d54b82be.png


有没有很熟悉?这个值就是我们前面拿到的 $_ts 变量,在开头就可以看到是将 window.$_ts 赋值给了 _$sX

bf2125b761e747f16e80885de16f3c9f.png

继续走,会走到以下逻辑中:

e4f653c71eb4ea412537bf74d6c7ad5c.png

这里会遇到六个数组,他们都已经有值了,所以我们得找到他们是咋来的,任意搜索其中一个数组名称,会找到定义和赋值的地方:

9cb371f15c0bce4a1e73a3e7a4715659.png

0d8594065ada87a913939310f65c739b.png


赋值明显是调用了 _$rv 方法,再搜 _$rv 方法,发现是开头就调用了:

10757c69ab584cbb1f84ebe817a1d6a3.png

后续没有什么特别的,一直单步,最后有个 join('') 操作,就生成了假 Cookie:

e12d2d9f42a18394483608cca3723576.png


接 下来是生成 Cookie 的名字 FSSBBIl1UgzbN7N80T ,然后将 Cookie 赋值给 document.cookie ,然后又向 localStorage 里面的 $_ck 赋了个值, localStorage 的内容可以直接复制下来,没有太大影响。

953b9b1d6d7693458765ee53faef7081.png


真 Cookie 生成逻辑

单步跟真 Cookie,在本文中也就是 _$ZN(768, 1);,可以看到开始进入了无穷无尽的 if-else 控制流:

e113b04baff306a9c82792998f21f465.png

这里本地应该怎样处理呢?我的做法是以 _$Hn 和其值命名函数,function _$Hn768(){} 就表示所有走 768 号控制流的方法,继续跟,生成真 Cookie 的方法基本上在 747 号控制流,后续我们主要以 747 号控制流的各个步骤来看,747 号控制流扣出来的代码大致如下:

523b4035c7b23b8015f9d699aa0c168f.png

取假 Cookie

单步跟 747 号控制流,会有个进入第 709 号控制流的步骤,会取先前生成的假 Cookie,经过一系列操作之后返回一个数组:

f8084468ed1f7776a41d8699836f952d.png

c242fd8546de196460756c58d0f7d0d1.png


至此我们在本地同步扣的代码,如果正常的话,返回的数组也应该是一样的(后续的数据就不一样了,有一些时间戳之类的参数参与运算):

a7b74f6854abacf6790f66ed325f0295.png

自动化工具检测

继续跟 747 号控制流,会进入 268 号控制流,接着进入 154 号控制流,这里面会针对自动化工具做一些检测,如下图所示:

817b7eb5ccea6bc78466c51f33295814.png

d712ffb64bf316a9acce71efdcd09003.png


这里定义了一个变量 _$iL,检测不通过就是1,后续又把这个变量赋值给了 _$aW,所以我们本地保持一致,也为 false 即可(其实我们不用自动化工具的话,这一段检测就不用管直接返回 false 就行):

9b06a36a92bcf3c71542655bf74a035c.png

20 位核心数组

继续跟 268 号控制流,会进入 668 号控制流,668 号控制流就两个操作,一是生成一个 16 位数组,二是取 $_ts 里面的 4 个变量,加到前面的 16 位后面,组成一个 20 位数组,这 20 位数组的最后 4 位是瑞数核心,其中的映射关系搞错了请求是通不过的,在五代中这部分的处理逻辑会更加复杂。

90c54a8cc2293ca0a2b3e56b4b66add1.png

c2b095510fae9bba9ccbc88640b8ee0f.png


这里不是单纯的取 $_ts 里的键值对,你在扣代码的时候,你也许会发现怎么本地到这里取值的时候,取出来的不是数字,而是字符串呢?就像下面这种情况:

ce2b8479c8d44d5e45c599024aa9a74f.png

实际上我们最开始得到的 $_ts 值,是经过了二次处理的,我们以第一个 _$sX._$Xb 为例,直接搜索 _$sX._$Xb,可以发现这么一个地方:

f91ede74b6da2763a79610725affa92d.png

很明显这里给  _$sX._$Xb 重新赋值了一遍,我们可以看到等号右边,先取了一次 _$sX._$Xb,其值为 _$Rm,这和我们初始 $_ts 里面对应的值是一样的,然后我们就得再看看 _$sX["_$Rm"] 又是何方神圣,直接搜索发现是开头赋值了一个方法,通过调用这个方法来生成新的值:

8aa60acf560ab0a101041f9d92173fa3.png

另外其他三个值也是同样的套路,赋值的代码分别为:

_$sX._$Xb = _$sX[_$sX._$Xb](_$BH, _$DP);
_$sX._$oI = _$sX[_$sX._$oI](_$ZJ, _$DS)
_$sX._$EN = _$sX[_$sX._$EN]();
_$sX._$D9 = _$sX[_$sX._$D9](_$iL);

实际上应该是:

_$sX._$Xb = _$sX["_$Rm"](_$BH, _$DP);
_$sX._$oI = _$sX["_$Nw"](_$ZJ, _$DS)
_$sX._$EN = _$sX["_$Uh"]();
_$sX._$D9 = _$sX["_$ci"](_$iL);

进一步来说,实际上是:

_$sX._$Xb = _$1k(_$BH, _$DP);
_$sX._$oI = _$jH(_$ZJ, _$DS)
_$sX._$EN = _$9M();
_$sX._$D9 = _$oL(_$iL);

静态分析没问题,我们可以先固定下来,但是实际应用当中这些值都是动态的,那我们应该怎么处理呢?先来多看几个对比一下找找规律:

371587f81647e6cf3980b23192d9836a.png

6faf87dbef1e477f428422deb086ee3f.png


可以发现每次对应的位次都不一样,但是实际上相同位置的方法点进去都是一样的,也就是说,变的只有方法名和变量名,实现的逻辑是不变的,所以我们只要知道了这四个值分别对应的位置,就能够拿到正确的值,在本地,我们就可以这样做:

1、先利用正则匹配出这四个值,如:[_$sX._$Xb, _$sX._$oI, _$sX._$EN, _$sX._$D9]

207fddb46eb9c810cbd26c4b8669f570.png


2、再匹配出 VM 代码开头的 20 个赋值的语句,如:_$sX._$RH = _$wI; _$sX._$i5 = _$n5; 等;

9f80b8365bc33d95fe40de38446992d5.png


3、然后通过 $_ts 取这四个值对应的值,相当于:_$sX._$Xb = _$ts._$Xb = _$Rm;然后再找这四个值所定义的方法在 20 个赋值语句中的位置,相当于:查找 _$sX._$Rm = _$1k; 在 20 个赋值语句中的位置为 7(索引从 0 开始)

6a44d08725c6c30825fb612d2972e403.png

4、我们知道了这四个方法在 20 个赋值语句中的位置,那么我们直接匹配本地对应位置的名称,进行动态替换即可,当然前提是咱们本地已经扣了一套代码出来了:

86f226bb1ebfde5bd7d3f564097d874a.png

e1b7bf8a65b33570df1e4b6366faa71e.png


经过这样处理后,就能够保证这四个值的准确性了。

其他用到 $_ts 值的地方

除了上面说的 20 位数组里用到了 4 个 $_ts 的值以外,还有其他地方有 7 个值也用到了,直接搜索就能定位,这 7 个值相对较简单,每次都是固定取 $_ts 里面的第 2、3、4、15、16、17、19 位的值,同样的,找到对应位置,进行动态替换即可:

20274abcb1d701297d3769adfb958303.png

注意事项

特别注意 VM 代码开头,会直接调用执行一些方法,某些变量的值就是通过这些方法生成的,当你一步一步跟的时候发现某些参数不对,或者没有,那么就得注意开头这些方法了,可能一开始就已经生成了。

464ebb139d670d56128b01a5d3eb687a.png

后缀 MmEwMD 生成逻辑

后续的其他 XHR 请求中,都带有一个后缀,这个后缀的值同样是由 JS 生成的,每次都会变化,当然不同网站,后缀名不一定都是一样的,本例中是 MmEwMD,先下一个 XHR 断点,当 XHR 请求中包含了 MmEwMD= 时就断下,然后刷新网页:

89b2d26d035e3fbe120dc3fbe78f118e.png

可以看到后传入 l.open() 的 URL 还是正常的,断下后到 l.send() 就带有后缀了,再看 l.open() 其实就是 xhr.open(),明显和正常的有区别,同样这个方法也在 VM 代码里,应该是重写了方法,可以和正常的做对比:

1883564d53f4ffec5541259be8143d18.png

跟到 VM 代码里去看看,经过了 _$sd(arguments[1]) 方法就变成了带有后缀的完整链接了:

38b00db03e0a4fcc7ac1b285c9b20290.png

跟进 _$sd 方法,前面都是对 url 做一些处理,后面有个进入第 779 号控制流的流程,实际上就是原来我们生成 Cookie 的步骤,跟一下就行了。

e3f394e1ccfa558e87855a47685ebe9e.png

善用 Watch 跟踪功能

11ede2414a29b2bcccc6a6ebb9b01d17.png


开发者工具的 Watch 功能能够持续跟踪某个变量的值,对于这种控制流很多的情况,设置相应的变量跟踪,能够让你知道你现在处于哪个控制流中,以及生成的数组的变化,不至于跟着跟着不知道到哪一步了。

结果验证

如果整个流程没问题,代码也扣得正确,携带正确的 Cookie 和正确的后缀,就能成功访问:

83396494daf4f9b9af3648d740a273a9.png

80ac0608dee4f9729f0f08d606efa3b7.png

16e8171349d188d8be79a8431ee1f012.png

End

崔庆才的新书《Python3网络爬虫开发实战(第二版)》已经正式上市了!书中详细介绍了零基础用 Python 开发爬虫的各方面知识,同时相比第一版新增了 JavaScript 逆向、Android 逆向、异步爬虫、深度学习、Kubernetes 相关内容,‍同时本书已经获得 Python 之父 Guido 的推荐,目前本书正在七折促销中!

内容介绍:《Python3网络爬虫开发实战(第二版)》内容介绍

05639d7088bf3120ce22d3ce2c0bbfdc.jpeg

扫码购买

159e0d3513f829e8e914f5e8af640028.png

a4b845770180b1efb2dab9c588bed1b9.png

点个在看你最好看

56c96064ab9167e082f1db154bdd79ef.png

原网站

版权声明
本文为[VIP_CQCRE]所创,转载请带上原文链接,感谢
https://blog.csdn.net/u010467643/article/details/125630873