Js逆向分析+Python爬虫结合

Source

JS逆向分析+Python爬虫结合

特别声明📢:本教程只用于教学,大家在使用爬虫过程中需要遵守相关法律法规,否则后果自负!!!

  • 完整代码地址Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/python-demo/spider-demo/spider-js
  • 大家如果觉得还不错的话,欢迎star⭐️哦

1 概念

有时我们通过Python爬虫爬取数据会发现服务器返回的是密文数据,我们拿到密文之后,需要对它进行解密才能转换为可用数据。

  • 前端对服务器返回的密文数据是通过JS来进行解密的,这时我们就需要分析并找到前端解密的代码,应用到我们的爬虫,达到爬虫抓取并解密数据的效果。
  • 总结:JS逆向分析,大致就是我们 ①分析前端JS代码 + ②通过JS代码解密服务器返回的密文数据。

2 JS逆向分析

以烯牛平台数据为例,我们登录平台,访问目前国内最新赛道,服务发现服务器返回的都是加密后数据,这时我们就需要使用XHR断点,一步步进行逆向分析。
在这里插入图片描述

  • 平台链接:https://www.xiniudata.com/industry/newest?from=data

思路分析 & 定位JS文件

在这里插入图片描述

  1. 可以看到服务器返回回来的数据在d字段中,这种命名太过常见,因此我们不能通过关键字定位搜索对应的js代码。
  2. 因为返回的是json数据,前端js肯定会进行解码,所以我们可以搜索与JSON.parse()有关的方法,找到对应解码位置。
  3. f12打开开发者工具,抓请求包,找到请求发起的js文件,点击进入
    在这里插入图片描述

格式化JS代码 & 打XHR断点

  1. 点击进入js代码后,点击代码片段左下角的花括号,格式化js代码,同时根据上面的分析,搜索JSON.parse方法
    在这里插入图片描述
  2. 可以看到搜索结果有11个,但此处我们应该找入参由他自定义解析出的数据,不要选成了由内置的函数解析的数据,如:l = JSON.stringfy(n),这种就该放过,不打断点。
    在这里插入图片描述
  3. y由前端自定义的方法而来,我们应该在此处打断点,因为这里很可能是解码的js部分。
    在这里插入图片描述

继续寻找,直到整个js文件搜索完成。最后我们在第30行、第38行为其打上断点。
在这里插入图片描述

刷新页面 & 移除无用断点

断点我们已经打好了,下面就需要我们刷新页面或者下滑,继续请求数据,以触发XHR断点。

  1. 刷新页面,触发断点
    在这里插入图片描述
  2. 我们发现断点走在了第30行的位置,这样我们就可以把其他断点去掉
    在这里插入图片描述

控制台 & 逆向分析JS

  1. 点击下一步,让程序往下执行一步,然后观察返回的数据是否是我们所需要的明文数据:
    在这里插入图片描述
  2. 来到控制台打印m的值,观察是否是我们需要的数据
    在这里插入图片描述
  3. 上面m发现是我们的个人信息,并非是我们所需要的数据,因此可直接跳过当前断点
    在这里插入图片描述
  4. 点击resume跳过断点后,发现程序再次走到JS代码的第30行。此时我们单步往下走一行,让程序计算出m的值
    在这里插入图片描述
  5. 控制台输入m,回车,观察控制台返回的是否是我们所需的数据
    在这里插入图片描述

可以看到,成功解析出明文数据,表明断点出的这个方法就是前端的JS解密逻辑处。

定位前端代码 & 完善JS

  1. 新建逆向JS代码,并拷贝这段JS代码
var d = Object(u.a)(s)
 , y = Object(u.b)(d)
 , m = JSON.parse(y);

在这里插入图片描述

  1. 控制台输入对应函数名,获取对应函数详细信息

可以看到上面的JS代码,我们并不知道Object(u.a)是什么意思,那么我们就放行,单步执行断点,待断点执行到Object(u.a)之后,就可以从控制台获取其具体值
在这里插入图片描述

  1. 控制台获取函数具体值:Object(u.a),点击控制台返回结果,查看函数详情
    在这里插入图片描述
  2. 拷贝函数到我们逆向的JS代码
    在这里插入图片描述
    我们逆向的JS代码就成了:
// 将u.a替换为了d1
var d = Object(d1)(s)
 , y = Object(u.b)(d)
 , m = JSON.parse(y);

function d1(e) {
    
      
    var t, n, r, o, i, a, u = "", c = 0;
    for (e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ""); c < e.length;)
        t = _keyStr.indexOf(e.charAt(c++)) << 2 | (o = _keyStr.indexOf(e.charAt(c++))) >> 4,
            n = (15 & o) << 4 | (i = _keyStr.indexOf(e.charAt(c++))) >> 2,
            r = (3 & i) << 6 | (a = _keyStr.indexOf(e.charAt(c++))),
            u += String.fromCharCode(t),
        64 != i && (u += String.fromCharCode(n)),
        64 != a && (u += String.fromCharCode(r));
    return u
}

重复此步骤,不断完善我们自己新建的JS代码,直到能正确解析出服务器返回的密文数据。

  • 服务器返回的密文数据获取:
    在这里插入图片描述
  • 拷贝到自己的逆向JS代码,然后执行,观察是否能解析成功
    在这里插入图片描述
  • 执行JS代码,解析成功
    在这里插入图片描述

3 JS+Python爬虫结合实战

curl自动生成Python爬虫

  1. 复制请求为curl

在这里插入图片描述

  1. 将curl转换为Python代码

网站链接:https://spidertools.cn/#/curl2Request
在这里插入图片描述

调用execjs执行我们编写的逆向JS

Python代码和js逆向代码都就绪了,剩下的就是我们通过execjs在Python代码中使用了。

全部python代码

demo01-JS逆向入门.py:

import requests
import json
# 执行命令安装:pip install pyExecJs
import execjs


headers = {
    
      
    "accept": "application/json",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "content-type": "application/json",
    "origin": "https://www.xiniudata.com",
    "priority": "u=1, i",
    "referer": "https://www.xiniudata.com/industry/newest?from=data",
    "sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Microsoft Edge\";v=\"128\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"macOS\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
}
cookies = {
    
      
    "btoken": "ZOH2JO8CQ3CUE1YH9PC3K6TG1Z3HCEAE",
    "hy_data_2020_id": "192383e583c10e1-06189fe412f809-7e433c49-1484784-192383e583d26f6",
    "hy_data_2020_js_sdk": "%7B%22distinct_id%22%3A%22192383e583c10e1-06189fe412f809-7e433c49-1484784-192383e583d26f6%22%2C%22site_id%22%3A211%2C%22user_company%22%3A105%2C%22props%22%3A%7B%7D%2C%22device_id%22%3A%22192383e583c10e1-06189fe412f809-7e433c49-1484784-192383e583d26f6%22%7D",
    "Hm_lvt_42317524c1662a500d12d3784dbea0f8": "1727520463,1727571559",
    "HMACCOUNT": "CA33EC3F1CCD3E5F",
    "utoken": "Z1MNZUNI27IG3HYGMABLAIRRP67D1DF4",
    "username": "Clay",
    "Hm_lpvt_42317524c1662a500d12d3784dbea0f8": "1728100324"
}
url = "https://www.xiniudata.com/api2/service/x_service/person_industry_list/list_industries_by_sort"
data = {
    
      
    "payload": "LBc3V0I6ZGB5bXsxTCQnPRBuDgQVcDhbICcmb2x3AjI=",
    "sig": "45B0ECB73CAE7AEA531F5A39B29023A0",
    "v": 1
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, data=data)

# 解析json响应,获取加密后数据
# print(response.json()["d"])

# 调用我们编写好的逆向JS函数,解密数据
decode_data = execjs.compile(open('./demo01.js', 'r', encoding='utf-8').read()).call('decode_data', response.json()["d"])
print(decode_data)

如果出现报错:execjs not found,执行下面命令安装:

pip install pyExecJs
全部逆向js代码

js解密代码,我们无需理解,直接拷贝即可

// s = '服务器返回的加密数据'
function decode_data(encode_data) {
    
      
    var d = Object(d1)(encode_data)
        , y = Object(d2)(d)
        , m = JSON.parse(y);
    rte = m
    return rte
}

function d1(e) {
    
      
    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var t, n, r, o, i, a, u = "", c = 0;
    for (e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ""); c < e.length;)
        t = _keyStr.indexOf(e.charAt(c++)) << 2 | (o = _keyStr.indexOf(e.charAt(c++))) >> 4,
            n = (15 & o) << 4 | (i = _keyStr.indexOf(e.charAt(c++))) >> 2,
            r = (3 & i) << 6 | (a = _keyStr.indexOf(e.charAt(c++))),
            u += String.fromCharCode(t),
        64 != i && (u += String.fromCharCode(n)),
        64 != a && (u += String.fromCharCode(r));
    return u
}

function d2(e) {
    
      
    _p = "W5D80NFZHAYB8EUI2T649RT2MNRMVE2O";
    for (var t = "", n = 0; n < e.length; n++) {
    
      
        var r = _p.charCodeAt(n % _p.length);
        t += String.fromCharCode(e.charCodeAt(n) ^ r)
    }
    return t = _u_d(t)
}

function _u_d(e) {
    
      
    for (var t = "", n = 0, r = 0, o = 0, i = 0; n < e.length;)
        (r = e.charCodeAt(n)) < 128 ? (t += String.fromCharCode(r),
            n++) : r > 191 && r < 224 ? (o = e.charCodeAt(n + 1),
            t += String.fromCharCode((31 & r) << 6 | 63 & o),
            n += 2) : (o = e.charCodeAt(n + 1),
            i = e.charCodeAt(n + 2),
            t += String.fromCharCode((15 & r) << 12 | (63 & o) << 6 | 63 & i),
            n += 3);
    return t
}

验证

执行Python爬虫代码,完整能否正确抓取并解析数据

在这里插入图片描述

完整代码

Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/python-demo/spider-demo/spider-js