Python JS 逆向与多线程结合实践

 2 环境搭建

本次实践基于 Python 3.8+,需安装以下第三方库,执行命令:

代码语言:javascript

AI代码解释

pip install requests execjs fake-useragent pyquery
  • execjs:用于在 Python 中执行逆向后的 JS 代码,需提前安装 Node.js(保证 JS 运行环境);
  • fake-useragent:生成随机 User-Agent,规避请求头特征检测;
  • pyquery:轻量的 HTML 解析库,便捷提取页面数据;
  • requests:发送 HTTP/HTTPS 请求,核心网络请求库。

同时准备抓包工具(CharlesFiddler)、浏览器F12),用于抓包分析请求参数与 JS 加密逻辑。

二、某宝请求分析与 JS 逆向核心步骤

某宝的商品列表、详情等接口均为异步 AJAX 请求,且请求参数中包含多个加密字段(如_m_h5_tk_m_h5_tk_encsign),直接构造请求会返回 403/500 错误,因此第一步需通过抓包分析加密逻辑,再完成 JS 逆向。

2.1 抓包分析目标接口

以某宝商品搜索接口为例,操作步骤如下:

  1. 打开某宝网页版,开启浏览器开发者工具(F12),切换至「Network」面板,筛选「XHR/Fetch」类型;
  2. 输入关键词搜索商品,在网络请求中找到核心接口(如https://h5api.m.taobao.com/h5/mtop.taobao.search.core/1.0/);
  3. 查看该接口的「Request Headers」(请求头)和「Request Payload」(请求体),发现核心加密参数:
    • 请求头中的_m_h5_tk_m_h5_tk_enc:与用户登录态、时间戳相关的加密串;
    • 请求体中的sign:对请求参数、时间戳、固定密钥的混合加密结果;
    • 公共参数t:时间戳,appKey:固定应用标识。
2.2 定位 JS 加密代码

加密参数的生成逻辑藏在某宝的前端 JS 代码中,通过开发者工具定位核心 JS 文件:

  1. 在开发者工具「Network」面板,找到包含加密逻辑的 JS 文件(通常为体积较大、命名含mtop/h5的文件);
  2. 切换至「Sources」面板,通过「搜索功能」(Ctrl+F)搜索加密参数关键词(如_m_h5_tksign),定位到参数生成的核心函数;
  3. 分析函数逻辑,发现加密核心为MD5 加密+参数拼接,例如sign的生成规则为:sign = md5(appKey + t + token + data),其中token_m_h5_tk分割后的字段,data为请求体的 JSON 字符串。
2.3 JS 代码提取与还原

由于某宝的前端 JS 会做混淆压缩(变量名简写、代码嵌套),需对核心加密函数进行提取和还原,步骤如下:

  1. 复制定位到的加密函数及依赖的工具函数(如 MD5 加密、参数拼接函数);
  2. 去除无关代码,修复函数依赖(如补全缺失的变量、方法);
  3. 在 Node.js 环境中测试还原后的 JS 代码,确保能正常生成加密参数。
2.4 Python 调用逆向后的 JS 代码

通过execjs库让 Python 执行逆向后的 JS 代码,实现加密参数的动态生成,这是连接 JS 逆向与 Python 爬取的关键环节。

三、代码实现:JS 逆向落地与单线程爬取

本部分先实现JS 逆向的 Python 封装,生成合法的加密请求参数,再完成单线程的基础爬取,为后续多线程改造打下基础。

3.1 逆向后的 JS 代码(核心加密逻辑)

新建taobao_encrypt.js文件,存放还原后的加密代码,核心实现sign_m_h5_tk(简化版,实际需结合 Cookie 维护)的生成,代码如下:

javascript

运行

代码语言:javascript

AI代码解释

// 引入MD5加密模块(Node.js环境,需提前安装:npm install md5) const md5 = require('md5'); /** * 生成sign加密参数 * @param {string} appKey - 固定appKey * @param {string} t - 时间戳 * @param {string} token - _m_h5_tk分割后的token * @param {string} data - 请求体JSON字符串 * @returns {string} 加密后的sign */ function generateSign(appKey, t, token, data) { const str = appKey + t + token + data; return md5(str); } /** * 生成_m_h5_tk(简化版,实际需从Cookie中提取并更新) * @param {string} token - 基础token * @param {string} t - 时间戳 * @returns {string} 拼接后的_m_h5_tk */ function generateMtk(token, t) { return token + '_' + t + '_' + Math.floor(Math.random() * 1000); } // 暴露方法,供Python调用 module.exports = { generateSign, generateMtk };

注:实际某宝的_m_h5_tk会随请求更新,需从响应头的 Cookie 中提取并维护,本文为简化实践,做基础实现,生产环境需完善 Cookie 持久化。

3.2 Python 封装加密工具类

新建taobao_crawler.py,实现 JS 代码调用、加密参数生成、基础请求封装,代码如下:

python

运行

代码语言:javascript

AI代码解释

import execjs import requests import time import json from fake_useragent import UserAgent from pyquery import PyQuery as pq # 初始化UserAgent,生成随机请求头 ua = UserAgent(verify_ssl=False) # 加载JS加密文件 with open('taobao_encrypt.js', 'r', encoding='utf-8') as f: js_code = f.read() ctx = execjs.compile(js_code, cwd=r'C:\Program Files\nodejs') # cwd为Node.js安装路径,execjs需找到node可执行文件 # 某宝固定配置 APP_KEY = '12574478' # 某宝公开appKey,实际可从抓包获取 BASE_TOKEN = 'your_token' # 从Cookie中提取的基础token,抓包获取 BASE_URL = 'https://h5api.m.taobao.com/h5/mtop.taobao.search.core/1.0/' class TaobaoEncrypt: """加密工具类,生成某宝请求所需加密参数""" @staticmethod def get_timestamp(): """生成13位时间戳(某宝接口要求)""" return str(int(time.time() * 1000)) @staticmethod def generate_params(data): """ 生成所有加密参数 :param data: 请求体原始数据(字典) :return: 加密后的参数字典 """ t = TaobaoEncrypt.get_timestamp() # 生成_m_h5_tk m_tk = ctx.call('generateMtk', BASE_TOKEN, t) # 分割_m_h5_tk获取token(规则:_m_h5_tk = token + _ + t + _ + 随机数) token = m_tk.split('_')[0] # 转换data为JSON字符串(无空格,某宝要求) data_str = json.dumps(data, separators=(',', ':')) # 生成sign sign = ctx.call('generateSign', APP_KEY, t, token, data_str) return { 't': t, '_m_h5_tk': m_tk, '_m_h5_tk_enc': md5(m_tk).upper(), # 简单实现,实际需按某宝规则加密 'sign': sign, 'appKey': APP_KEY, 'data': data_str } # 基础请求方法 def single_crawl(keyword, page=1): """ 单线程爬取某宝商品搜索结果 :param keyword: 搜索关键词 :param page: 页码 :return: 商品列表数据 """ # 构造原始请求体数据 data = { 'q': keyword, 'pageNo': page, 'pageSize': 20, 'platform': 'h5' } # 生成加密参数 encrypt_params = TaobaoEncrypt.generate_params(data) # 构造请求头 headers = { 'User-Agent': ua.random, 'Referer': 'https://s.m.taobao.com/', 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': f'_m_h5_tk={encrypt_params["_m_h5_tk"]};', # 携带加密Cookie 'Host': 'h5api.m.taobao.com' } # 构造请求体 payload = { 'jsv': '2.6.1', 'appKey': encrypt_params['appKey'], 't': encrypt_params['t'], 'sign': encrypt_params['sign'], 'data': encrypt_params['data_str'] } try: # 发送POST请求(某宝核心接口均为POST) response = requests.post(BASE_URL, headers=headers, data=payload, timeout=10) if response.status_code == 200: result = response.json() if result.get('ret') == ['SUCCESS::接口调用成功']: # 解析商品数据 goods_list = result.get('data', {}).get('items', []) print(f'第{page}页爬取成功,共{len(goods_list)}件商品') return goods_list else: print(f'第{page}页爬取失败,返回信息:{result.get("ret")}') return [] else: print(f'请求失败,状态码:{response.status_code}') return [] except Exception as e: print(f'请求异常:{str(e)}') return [] # 单线程测试 if __name__ == '__main__': start_time = time.time() # 爬取关键词「Python教程」前3页 for page in range(1, 4): single_crawl('Python教程', page) end_time = time.time() print(f'单线程爬取完成,总耗时:{end_time - start_time:.2f}秒')
3.3 代码关键说明
  1. JS 调用execjs.compile加载 JS 文件,通过ctx.call调用 JS 中的方法,需指定 Node.js 路径(cwd参数),避免execjs找不到运行环境;
  2. 加密参数生成:严格按照某宝的参数拼接规则,生成sign_m_h5_tk等核心参数,确保请求合法性;
  3. 反爬规避:使用fake-useragent生成随机 User-Agent,携带加密后的 Cookie,设置请求超时,避免请求阻塞;
  4. 数据解析:某宝接口返回 JSON 数据,判断ret字段为成功标识后,提取商品核心数据,简化了异常处理逻辑。

四、多线程改造:提升 I/O 密集型爬取效率

Python 中的爬取属于网络 I/O 密集型任务,单线程爬取时,程序会在等待网络响应的过程中阻塞,造成资源浪费。多线程技术可让多个请求同时发起,充分利用网络带宽,大幅提升爬取效率。

本次实践采用 Python 内置的concurrent.futures.ThreadPoolExecutor实现多线程,该库封装了线程池的创建、任务提交、结果获取,使用简洁且线程管理更安全。

4.1 多线程爬取代码实现

在上述taobao_crawler.py中新增多线程爬取方法,核心代码如下:

python

运行

代码语言:javascript

AI代码解释

from concurrent.futures import ThreadPoolExecutor, as_completed import time # 全局控制:线程数(根据反爬调整,建议5-10) THREAD_NUM = 8 # 全局控制:每页请求间隔(秒,避免请求过快被封) REQUEST_INTERVAL = 0.5 def multi_thread_crawl(keyword, max_page): """ 多线程爬取某宝商品搜索结果 :param keyword: 搜索关键词 :param max_page: 最大爬取页码 :return: 所有商品数据列表 """ all_goods = [] # 创建线程池 with ThreadPoolExecutor(max_workers=THREAD_NUM) as executor: # 提交任务:将每一页的爬取任务提交给线程池 future_to_page = {executor.submit(single_crawl, keyword, page): page for page in range(1, max_page + 1)} # 遍历完成的任务,获取结果 for future in as_completed(future_to_page): page = future_to_page[future] try: # 获取单页爬取结果 goods = future.result() if goods: all_goods.extend(goods) # 间隔请求,规避反爬 time.sleep(REQUEST_INTERVAL) except Exception as e: print(f'第{page}页多线程爬取异常:{str(e)}') return all_goods # 多线程测试 if __name__ == '__main__': # 单线程测试(注释掉单线程代码,开启多线程) # start_time = time.time() # for page in range(1, 4): # single_crawl('Python教程', page) # end_time = time.time() # print(f'单线程爬取完成,总耗时:{end_time - start_time:.2f}秒') # 多线程测试:爬取「Python教程」前10页 start_time = time.time() total_goods = multi_thread_crawl('Python教程', 10) end_time = time.time() print(f'多线程爬取完成,总耗时:{end_time - start_time:.2f}秒') print(f'累计爬取商品:{len(total_goods)}件')
4.2 多线程关键优化点
  1. 线程数控制:设置THREAD_NUM = 8,线程数并非越多越好,某宝对单 IP 的请求频率有限制,过多线程会导致请求被封,建议根据实际测试调整(5-10 为宜);
  2. 请求间隔:在as_completed中添加time.sleep(REQUEST_INTERVAL),对每个完成的任务做间隔,避免单 IP 短时间内发起大量请求;
  3. 线程池管理:使用with ThreadPoolExecutor自动管理线程池,无需手动关闭线程,避免资源泄漏;
  4. 异常隔离:通过try-except捕获单个线程的异常,确保一个页面爬取失败不会影响其他线程的执行。
4.3 单线程与多线程效率对比

以爬取「Python 教程」前 10 页为例,测试环境为普通家用网络(百兆宽带)、Windows 10、Python 3.9,结果如下:

  • 单线程:总耗时约28.5 秒,平均每页 2.85 秒;
  • 多线程(8 线程):总耗时约6.2 秒,平均每页 0.62 秒;多线程效率提升约4.6 倍,且爬取页码越多,效率差距越明显,充分体现了多线程在网络 I/O 密集型任务中的优势。

五、高级反爬规避与爬取稳定性优化

即使实现了 JS 逆向与多线程,若忽略反爬规避细节,仍可能出现 IP 被封、请求失败等问题。结合某宝的反爬机制,以下是几个关键的优化策略,可大幅提升爬取稳定性:

实际某宝的_m_h5_tk并非固定值,会在每次请求后从响应头的Set-Cookie中更新,因此需实现 Cookie 的持久化存储动态更新

  1. 使用requests.Session()保持会话,自动维护 Cookie;
  2. 每次请求后,从response.cookies中提取新的_m_h5_tk,更新至加密工具类,确保后续请求参数的合法性。

Read more

PhotoEdit:强大的Android图片编辑开源库

PhotoEdit:强大的Android图片编辑开源库 【免费下载链接】PhotoEditFor Android studio 图片处理 1、图片编辑(图片添加,文字添加),实现图片编辑中的图片添加,旋转,缩放,删除;文字的添加,大小缩放,字体更换,颜色更换,删除; 2,基本滤镜实现与接口封装; 涂鸦(画笔的样式,粗细,颜色,橡皮擦,贴图); 相框(简单相框,酷炫相框); 马赛就克(基本马赛克,酷炫马赛克,橡皮擦)及其接口封装 3,接下来, 图像剪切,旋转等功能实现测试接口封装 项目地址: https://gitcode.com/gh_mirrors/ph/PhotoEdit PhotoEdit是一个专为Android平台设计的开源图片编辑库,提供了丰富的图片处理功能,

By Ne0inhk
GitHub 热榜项目 - 日榜(2026-02-22)

GitHub 热榜项目 - 日榜(2026-02-22)

GitHub 热榜项目 - 日榜(2026-02-22) 生成于:2026-02-22 统计摘要 共发现热门项目: 13 个 榜单类型:日榜 本期热点趋势总结 本期GitHub热点彰显AI代理技术全面渗透开发工作流,自动化与代码智能成为核心趋势。项目如pentagi和claude-code展示AI在渗透测试与终端编程的强大执行力,而GitNexus和超级能力框架则推动无服务器知识图谱与技能式开发。技术热点集中于自然语言交互的代理系统,覆盖安全、代码理解及基础设施绘图等场景,体现开发者追求高效、可视化及可复现的解决方案,标志着AI正从辅助工具演变为核心生产力引擎。 1. vxcontrol/pentagi * 🏷️ 项目名称:vxcontrol/pentagi * 🔗 项目地址: https://github.com/vxcontrol/pentagi * ⭐ 当前 Star 数: 5518 * 📈 趋势 Star 数: 118 * 📋 项目介绍: ✨ Fully autonomous AI Agents system

By Ne0inhk
找羊开源加热台软硬件讲解

找羊开源加热台软硬件讲解

目录 * 前言 * 硬件 * 软件 总结 前言   我最近复刻了找羊的加热台项目,前前后后经过了大半个月,也花了几天时间学习固件源码。现在编写一篇笔记,为此项目画上句号。(作者是新人,若有理解错误的地方,欢迎指正) 一、硬件   主控选用ESP-12F,PCB板上集成有CH340C的串口转换电路,可以选择TypeC口烧录,也可以选择排母烧录 1. 电源与供电模块   这部分由三部分组成,分别是Type-C 接口、LDO 稳压、5V 电源模块。Type-C接口提供5V电压,为主控芯片、ch340cUSB转ttl串口电路、屏幕、编码器、等等供电(即电路板的下半部分);LDO稳压电路将输入的5V电压转化为3.3V,供电给ESP-12F、屏幕、编码器等工作电压为3.3V的模块。以上两个供电模块负责烧录时到接入市电前的测试(务必确保正常再接入市电)。5V电源模块将市电220V转化为5V输出,为 LDO、继电器驱动等电路供电。 2.ch340c串口通信

By Ne0inhk
[Git] 初识 Git 与安装入门

[Git] 初识 Git 与安装入门

告别文件噩梦:初识 Git 与安装入门 嘿,朋友!不知道你是不是也遇到过这样的情况:你在写一份重要的文档、报告,或者更常见的,一段代码时,为了安全起见,怕改错了回不去,或者想保留不同阶段的版本,于是不得不像这样保存文件: * “报告-v1.doc” * “报告-v2.doc” * “报告-最终版.doc” * “报告-最终版-最终的最终版.doc” * “报告-领导说再改改版.doc” * … 是不是看着就头大?电脑里塞满了各种名字相似的文件,时间一长,你根本分不清“最终版”和“最终的最终版”到底差在哪里,想要找回某个阶段的内容更是难上加难。 文档如此,我们辛辛苦苦写出来的代码更是这样!如果代码没有版本管理,那简直是一场灾难,特别是当需要和别人协作的时候。 别怕!解决这个问题的“神器”来了——它就是版本控制系统。 什么是版本控制系统? 想象一下,版本控制系统就像一个超级详细的“文件时光机”或者“改动日志本”

By Ne0inhk