公司信息建设库数据 使用调用堆栈的JS逆向爬虫

源代码仓库

惜哉剑气疏/programs_0https://gitee.com/zirui-shu/programs_0

项目前瞻

目标网站:

复制代码
https://www.jiansheku.com/search/enterprise/

爬取的是图中的公司信息数据。

此文章多讲解了调用堆栈的使用。

JS逆向过程实现

首先,老规矩:找到目标动态数据并构建基本的爬虫代码:

并且得到输出结果:

很显然,返回的300状态码是错误的,也就是说我们构建的基本爬虫代码是不够的。而在对文件进行观察之后,发现请求头有两个特殊的数据:

一个是标签 一个是时间戳。它会根据我们的时间动态生成两个数据以阻止我们的爬虫程序。

调出文件的调用堆栈:

前面两个导出函数不用管它。点击Promise.then后面的第一个函数链接,进入到发送请求的函数里,打上断点并刷新页面:

在右侧的作用域->本地可以看到:

说明这个t变量跟请求头数据生成有关。再结合刚刚在调用堆栈看到的v.request,以及在本地里所见:

基本可以确定所需要的函数就在v里面了。找出v数组里面对应方法的函数体打上断点(注意函数名包含request 关键字,包含response 的是响应与数据生成无关)。去掉之前在调用then时的断点,重新加载页面并不断调试断点,最终出现两个变量时停在图中的页面:

细心的你肯定发现了:在断点进行时到停止,只有左边这一个函数一直执行。那么,我们就可以去掉另一个函数的断点,并且把这个函数的断点放在e(t) 前,执行断点再找到其对应的函数体(注意!不换断点直接找可能会导致函数不对应):

找到了signtimestamp的生成方式。按照我前几篇文章讲的方法补充一个个函数和变量,生成对应的标签和时间戳变量值:

复制代码
gd = function (e) {
    var t = new Array
        , n = 0;
    for (var i in e)
        t[n] = i,
            n++;
    return t.sort()
}

_d = function e(t) {
    var n;
    if (Array.isArray(t)) {
        for (var r in n = new Array,
            t) {
            var o = t[r];
            for (var i in o)
                null == o[i] ? delete t[r][i] : Array.isArray(t[r][i]) && e(t[r][i])
        }
        return n = t,
            JSON.stringify(n).replace(/^(\s|")+|(\s|")+$/g, "")
    }
    return n = t && t.constructor === Object ? JSON.stringify(t) : t
}

md = function (e) {
    var t = gd(e)
        , n = "";
    for (var i in t) {
        var r = _d(e[t[i]]);
        null != r && "" != r.toString() && (n += t[i] + "=" + r + "&")
    }
    return n
}

const crypto = require('crypto');

function md5(str) {
    return crypto.createHash('md5').update(str, 'utf8').digest('hex');
}

pd = function (e, t, time) {
    var n = t + e + time;
    return n = md5(n)
}

function main(json_data){
    f = (new Date).getTime()
    headers = {}
    headers.timestamp = f
    headers.sign = (param = json_data,
        time = f,
        t = md(param),
        pd("ghaepVf6IhcHmgnk4NCTXLApxQkBcvh1", pd("mwMlWOdyM7OXbjzQPulT1ndRZIAjShDB", pd("ZuSj0gwgsKXP4fTEz55oAG2q2p1SVGKK", t, time), time), time))
    return headers
}

(补充:原图片中对应的e_data 变量与生成的基础爬虫代码的json_data 类似,直接写成一个函数的形参传入;还有pd 方法中的原fd 方法,最终调用的是一个MD5加密方法(可以使用工具验证),在此使用AI直接写一个加密函数调用)

最终,构建完整爬虫代码:

复制代码
import requests
import execjs

with open('JS.js', 'r', encoding='utf-8') as f:
    js_code = f.read()

cookies = {
    'Hm_lvt_03b8714a30a2e110b8a13db120eb6774': '1763301182,1763382265',
    'HMACCOUNT': '481441E951CF8E41',
    'Hm_lpvt_03b8714a30a2e110b8a13db120eb6774': '1763382808',
}

headers = {
    'accept': 'application/json, text/plain, */*',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
    'cache-control': 'no-cache',
    'content-type': 'application/json;charset=UTF-8',
    'devicetype': 'PC',
    'origin': 'https://www.jiansheku.com',
    'page': 'search-enterprise',
    'pragma': 'no-cache',
    'priority': 'u=1, i',
    'referer': 'https://www.jiansheku.com/',
    'sec-ch-ua': '"Chromium";v="142", "Microsoft Edge";v="142", "Not_A Brand";v="99"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-site',
    'sign': 'a454af043c83aa2fb735d937470d31d5',
    'timestamp': '1763382812705',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0',
    # 'cookie': 'Hm_lvt_03b8714a30a2e110b8a13db120eb6774=1763301182,1763382265; HMACCOUNT=481441E951CF8E41; Hm_lpvt_03b8714a30a2e110b8a13db120eb6774=1763382808',
}

json_data = {
    'eid': '',
    'achievementQueryType': 'and',
    'achievementQueryDto': [],
    'personnelQueryDto': {
        'queryType': 'and',
    },
    'aptitudeQueryDto': {
        'queryType': 'and',
        'nameStr': '',
        'aptitudeQueryType': 'and',
        'businessScopeQueryType': 'or',
        'filePlaceType': '1',
        'aptitudeDtoList': [
            {
                'codeStr': '',
                'queryType': 'and',
                'aptitudeType': 'qualification',
            },
        ],
        'aptitudeSource': 'new',
    },
    'page': {
        'page': 2,
        'limit': 20,
        'field': '',
        'order': '',
    },
}

response = requests.post('https://capi.jiansheku.com/nationzj/enterprice/page', cookies=cookies, headers=headers,
                         json=json_data)
pages = input('请输入页数:')
for i in range(1, int(pages) + 1):
    json_data['page']['page'] = i
    result = execjs.compile(js_code).call('main', json_data)
    headers['sign'] = result['sign']
    headers['timestamp'] = str(result['timestamp'])
    response = requests.post('https://capi.jiansheku.com/nationzj/enterprice/page', cookies=cookies, headers=headers,
                             json=json_data)
    print(response.json())

圆满结束。老规矩,提取关键数据交给读者...

相关推荐
泯泷2 小时前
Tiptap 深度教程(四):终极定制 - 从零创建你的专属扩展
前端·javascript·架构
局i2 小时前
vue简介
前端·javascript·vue.js
yqcoder3 小时前
vue2 和 vue3 中,列表中的 key 值作用
前端·javascript·vue.js
U***49833 小时前
前端TypeScript教程汇总,从基础到高级
前端·javascript·typescript
梵得儿SHI3 小时前
Vue 指令系统:事件处理与表单绑定全解析,从入门到精通
前端·javascript·vue.js·v-model·v-on·表单数据绑定·表单双向绑定
二川bro3 小时前
第45节:分布式渲染:Web Workers多线程渲染优化
开发语言·javascript·ecmascript
秋天的一阵风3 小时前
😱一行代码引发的血案:展开运算符(...)竟让图表功能直接崩了!
前端·javascript·vue.js
Hilaku4 小时前
npm scripts的高级玩法:pre、post和--,你真的会用吗?
前端·javascript·vue.js
mCell4 小时前
React 如何处理高频的实时数据?
前端·javascript·react.js