资讯动态

Python爬虫集合:七站点JS逆向与工程化实战指南

发布时间:2026/10/14 10:51:48 来源:尧图企业网站定制
简介面向爬虫逆向学习者、适用于毕业设计选题的 PythonJavaScript 案例合集整合网易易盾、极验滑块、同花顺、企查查、雪球网、1688、七麦数据等二十余个典型网站的逆向破解过程覆盖验证码识别、小程序反编译、RPC 解密、加速乐防护绕过等高频难点适合具备基础 Python 语法、希望进阶反爬对抗的中级开发者。压缩包共70个文件30个 Python 脚本为主体爬虫与解密调用代码30个 JavaScript 文件用于补环境、算法还原与签名生成辅以 JSON 配置、txt 说明和 README 笔记整体仅827KB目录按时间与平台模块化组织便于按需定位。目前已有727人学习下载虽代码量精简但每个案例均配有可运行脚本与说明读者可对照注释梳理目标站点的参数生成链路复用其中验证码处理、WebSocket/RPC 通信或小程序解包思路为自己项目的反爬突破提供直接参考。1. 一套能拆开用的 python 爬虫集合七个站点加 JS 逆向毕设和实战都够用把七个来源不同的采集脚本放进同一个仓库对做 python 爬虫方向毕业设计的人来说最怕的不是跑不起来而是不知道每一步在干什么。这套集合的价值在于它把 js 逆向、请求伪装、数据落库串成了一条能照着拆的链路。别把它当成品系统当参考实现来用更合适。资源里集成了 whggzy政务公开类、qiming企业服务类、mohurd建设行业数据类、endata经济数据类、1688批发数据类、cinfo资讯数据类、qq-m移动端资讯类这七个站点覆盖静态 HTML 抓取、JSON 接口解析、带签名参数的 JS 逆向三类常见场景。适合两类人一类是毕设选题在爬虫方向、需要短时间拿出多站点采集 Demo 的学生另一类是日常维护多个数据源、想抽公共模块复用代码的工程师。新手按目录走熟手可以直接看参数边界和踩坑记录。2. 工程化组织七个站点混跑前先把目录和会话拆干净拿到资源先别急着跑主脚本。这套集合的站点多最容易翻车的地方是“一个 Session 跑七个站”结果登录态互相污染、签名串平台。先把目录结构理清楚再谈抓取。2.1 目录布局把每个站点当独立模块来维护打开资源包先看目录。常见的组织方式是每个站点一个目录公共的请求、存储、配置抽到上层逆向脚本sign.js 这类留在站点内部。这样改站点的签名逻辑时不会误伤其他站。scrapers/ ├── configs/ # 站点公共配置 │ ├── ua_pool.py # UA 池 │ └── site_settings.py # 每个站点的 Header、超时、重试阈值 ├── storage/ # 落库与去重 │ ├── json_store.py # JSONL 调试期存储 │ └── mysql_store.py # 稳定期 MySQL 写入 ├── core/ # 公共能力 │ ├── http_client.py # 请求会话与重试逻辑 │ └── sign_executor.py # 加载并执行站点 JS 逆向脚本 ├── sites/ │ ├── whggzy/ │ │ ├── spider.py # 站点爬虫主流程 │ │ └── parser.py # HTML/JSON 解析 │ ├── qiming/ │ │ ├── spider.py │ │ ├── sign.js # 逆向出来的签名函数 │ │ └── decrypt.py │ ├── mohurd/ │ ├── endata/ │ ├── 1688/ │ ├── cinfo/ │ └── qq_m/ └── run_all.py # 按顺序启动所有站点这里的关键是sign.js 放在对应站点目录而不是公共目录。原因是每个站的签名算法不一样放在一起反而容易在执行时搞混上下文。configs 里的 site_settings.py 专门维护“哪些站要带动态 Token、哪些站要固定 Header”后续加站点只需要新增一个字典条目。2.2 请求会话重试、超时、代理收敛到公共模块七个站的反爬强度不同Header 要求也不同。尤其 Referer 和 Origin很多站点校验得比 UA 严格。公共模块不应该试图统一 Header只负责重试、超时、代理、UA 轮换这类通用能力。# core/http_client.py import random import time import requests class SmartClient: 每个站点一个实例避免 Session 串数据 def __init__(self, site_name, headersNone, proxiesNone): self.site_name site_name self.session requests.Session() # 基础 UA站点级配置会覆盖它 self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Accept: text/html,application/json, Accept-Language: zh-CN,zh;q0.9, }) if headers: self.session.headers.update(headers) if proxies: self.session.proxies.update(proxies) def get(self, url, paramsNone, retry3, **kwargs): for i in range(retry): try: # 指数退避先等 2 秒之后翻倍避免频繁触发限流 if i 0: time.sleep(2 ** i random.random()) resp self.session.get(url, paramsparams, timeout10, **kwargs) if resp.status_code in (403, 429): continue return resp except requests.RequestException as exc: print(f[{self.site_name}] 请求失败第 {i 1} 次: {exc}) raise ConnectionError(f[{self.site_name}] 重试结束仍然失败)逻辑说明构造时传入站点名和自定义 Header实例内部持有独立 requests.Session不会跨站污染 Cookie。get 方法里做了最多 retry 次的重试403 和 429 被当成反爬信号直接重试其他网络异常打印之后继续下一次。参数说明retry 默认 3想压紧一点设成 2timeout 设 10 秒动态接口通常 3 到 5 秒就返回10 秒足够proxies 参数留给需要走代理池的时候用不带也不影响调试。实际使用中把 UA 换成资源包 configs 里准备的那批别用默认值顶着跑。2.3 存数据JSONL 先行MySQL 按需接入调试阶段不建议直接上 MySQL。字段没稳定、表结构反复改来回迁移很痛苦。先用 JSONL 把每一条结果追加到文件等字段确认后再写 MySQL。# storage/json_store.py import json import os class JsonStore: def __init__(self, out_diroutput): self.out_dir out_dir os.makedirs(out_dir, exist_okTrue) def save_item(self, site, item): file_path os.path.join(self.out_dir, f{site}.jsonl) with open(file_path, a, encodingutf-8) as f: f.write(json.dumps(item, ensure_asciiFalse) \n)逻辑说明save_item 按站点名生成独立文件追加写入避免多站点数据混在一个文件里。ensure_asciiFalse 保证中文和特殊字符不会被转义成 \uXXXX方便人工复查。参数说明out_dir 默认是 output想分开存放就传不同目录每行一条 JSON 的标准 JSONL 格式可以后续直接用 pandas 读取做清洗。资源包里 qq-m 和 cinfo 的数据偏文本型用这个方式存比 SQL 字段更灵活。3. JS 逆向三连从定位签名参数到补环境执行这套资源里真正有门槛的是 qiming、1688、qq-m 三个站点的动态签名。其他站点大多直接返回 JSON不用碰 JS。做逆向讲究顺序先定位再执行别一上来就补环境。3.1 定位签名参数先全局搜索再下断点常见做法是先把页面里那个加密参数的值复制出来在开发者工具里用全局搜索CtrlShiftF去搜。搜不到就把参数名换一下再搜比如 sign 搜不到就搜 md5、bizParams 这类字段名。找到调用方之后再在 Sources 面板下断点。对于调试被反调的情况可以用 Hook 的方式定位签名的生成时机。把下面这段代码注入页面只要带 sign 字段的 JSON 被序列化就会触发断点// sites/qiming/hook_sign.js if (!window.__signHooked) { window.__signHooked true; const rawStringify JSON.stringify; JSON.stringify function (obj) { if (obj typeof obj object) { const keys Object.keys(obj); if (keys.includes(sign) || keys.includes(token)) { debugger; } } return rawStringify.apply(this, arguments); }; }逻辑说明重写 JSON.stringify在序列化对象时检查 key 列表里有没有 sign 或 token。有就触发 debugger浏览器会暂停在 Hook 函数这一行然后后退一步栈就能看到谁在调用 stringify加密函数通常就在调用链上层。参数说明keyword 数组可以根据实际情况扩展比如 bizSign、nonceStr。这套方案只做定位不改动页面原有逻辑刷新页面后 Hook 失效需要重新注入。资源包里三个带逆向需求的站点定位方式基本一致。3.2 两条执行路线execjs 与 Node 子进程怎么选拿到签名算法后在 Python 里执行 JS 是最后一步。最省事的方案是 execjs把 JS 文件读进来编译然后直接调用暴露出的函数# core/sign_executor.py import execjs class JsRunner: def __init__(self, js_path): with open(js_path, r, encodingutf-8) as f: self.ctx execjs.compile(f.read()) def sign(self, params): 调用 JS 文件里的 getSign 函数 params 必须是 JSON 可序列化的字典 return self.ctx.call(getSign, params)逻辑说明compile 时做了完整 ES5 语法的编译call 会进入 JS 运行时执行特定命名函数。调用前的 params 要先用 json.dumps 转换到可序列化否则 execjs 传参容易抛错。参数说明js_path 指向站点目录里的 sign.jsgetSign 对应 JS 里的导出函数名。如果你拿到的是 Node 风格的 module.exportsexecjs 默认不认需要在 JS 文件尾部补一行全局暴露// sign.js 底部 function getSign(params) { // 真实签名逻辑在这里 }至于 Node 子进程方案通常用在高强度补环境的场景。execjs 依赖本机 Node 运行时本地没装 Node 会直接报错这是大多数同学第一次跑资源包就翻车的原因。调试阶段 execjs 足够生产环境对性能有要求再考虑 Node 子进程。一条经验能用 execjs 解决就不上 JSDOM。补环境补到最后往往是补不清的优先看能不能绕过去。3.3 补环境window、navigator 缺失时的兜底方案跑 JS 时最常见报错是 “navigator is not defined” 或 “window is not defined”。原因是 JS 里用了浏览器全局对象而 Node 环境没有。这时候不要急着把整个 JSDOM 引进来先做轻量补环境// build_env.js 在 execjs.compile 之前拼接执行 globalThis.window globalThis; globalThis.navigator { userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), platform: Win32, language: zh-CN, }; globalThis.document { cookie: , referrer: , };逻辑说明把 globalThis 指向 window让 JS 里访问 window 时不会报未定义navigator 只留 userAgent、platform、language 三个常用字段document 只留 cookie 和 referrer。这样的环境能满足大部分纯计算类签名算法。参数说明如果 JS 里要读取真实 Cookie就把登录后的 Cookie 字符串填进 document.cookie如果 JS 里做了 DOM 操作那轻量补环境就不够用考虑用 JSDOM 或换一条逆向思路。资源包里的 1688 站点相关脚本属于这一挂动手前先判断它只是算参数还是要操作 DOM。4. 反爬对抗动态 Token、嵌套 JSON 与分页抓取whggzy 和 mohurd 这两个站点属于典型的“数据不加密但请求要带动态令牌”的类型真实工作量集中在 Token 维护和 Header 构造上。endata 则对数据结构解析有要求先讲 Token再讲解析。4.1 动态 Token拿到令牌再拼请求头这类站点的通用流程是先请求一个 Token 接口拿到令牌后拼进后续请求的 Header 或查询参数。Token 通常有有效期过期后接口返回 401 或带提示的 JSON。# sites/mohurd/spider.py import time class MohurdSpider: def __init__(self, client): self.client client self.token None def refresh_token(self): # 实际地址以资源包站点目录里的配置为准 resp self.client.get(https://example-domain/api/token) if resp.status_code 200: data resp.json() self.token data.get(token, ) else: time.sleep(3) self.refresh_token() def fetch_list(self, page1): if not self.token: self.refresh_token() headers {Authorization: fBearer {self.token}} resp self.client.get( https://example-domain/api/list, params{page: page, size: 20}, headersheaders, ) return resp.json()逻辑说明refresh_token 先请求令牌接口后续请求统一在 Header 里带 Authorization。列表接口的页码和每页条数通过 params 传Token 失效时再刷新一次。参数说明这里用递归方式重试 Token在异常频繁时容易变成死循环建议改成 while 循环或限制重试次数资源包里 whggzy 的脚本处理得更好直接参考它的循环写法。Token 过期判断不能只看状态码有些接口 HTTP 200 但业务字段 code 是 401要在解析时一并处理。4.2 嵌套 JSON先展平再入库不返工endata 返回的结构层级较深列表套对象对象里又套数组。直接按原始结构入库查询时写 SQL 会很痛苦。先把嵌套结构展平成扁平的 key-value再决定是不是要落表。# sites/endata/parser.py def flatten(prefix, data, result): if isinstance(data, dict): for k, v in data.items(): new_key f{prefix}.{k} if prefix else k flatten(new_key, v, result) elif isinstance(data, list): for idx, item in enumerate(data): new_key f{prefix}[{idx}] flatten(new_key, item, result) else: result[prefix] data return result逻辑说明字段名带层级语义比如 data.items[0].name取值和后续清洗都方便。数组带下标调试时能直接定位到具体元素。参数说明同一套 flatten 函数可以复用到 qiming 和 cinfo 的列表页。注意数字类型的值在展平后还是数字不会强制转字符串存储时保持类型一致性。资源包里 endata 的脚本还加了一层字段名重命名逻辑展平后再把 data.items[0].name 改成 item_name 这类短字段看个人习惯。4.3 翻页与详情分离先列表后详情的顺序约束cinfo 这类站点通常是列表页拿摘要和详情 ID完整内容在详情页。先跑列表页收集 ID再逐个请求详情页。顺序错了会导致大量重复请求。# sites/cinfo/spider.py class CinfoSpider: def fetch_all_detail(self, list_url, detail_url_template): # 第一步拿列表页 ID 列表 ids [] for page in range(1, 6): # 前 5 页足够调试 data self.client.get(list_url, params{page: page}).json() ids.extend(item[id] for item in data.get(list, [])) # 第二步按 ID 请求详情 for current_id in ids: detail_url detail_url_template.format(idcurrent_id) detail_data self.client.get(detail_url).json() yield detail_data逻辑说明先收集列表页 ID再循环请求详情两个阶段分离。防止翻页过程中详情页反爬参数变化导致列表和详情错乱。参数说明detail_url_template 用字符串格式化占位detail 接口的地址由资源包里的配置提供。列表页循环 5 页是调试值完整跑的时候按站点实际页数配置或者读接口返回的 total 字段计算页数。5. 避坑与排查Cookie、调试器断点和验证码的四类翻车现场资源包跑起来之后真正消耗时间的往往是环境问题而不是逻辑问题。这里挑四个高频坑每个都按“现象 → 原因 → 解决”记录都是实际调试时踩过的。5.1 Cookie 频繁失效跑半小时就断现象whggzy 跑一段时间后突然返回登录页 HTML或者接口提示 session 过期但代码本身没报错。原因站点侧的 Cookie 有效期通常只有十几分钟有的还绑 IP。爬虫长时间只用一个 Session到期后令牌没有被主动刷新请求自然落到登录态校验失败的分支。解决在 SmartClient 里加 Cookie 失效检测——响应状态码是 302、或返回内容里出现“登录”关键字时触发一次统一的 Cookie 更新流程。具体做法是把 refresh_token 和 Cookie 获取合并成一个方法在请求入口做一层判断。从那以后我每次遇到这类问题第一反应都是去看 Token 刷新逻辑是不是没被调用而不是怀疑解析代码。5.2 验证码突然出现频率一高就弹现象qiming 和 cinfo 的接口连续请求 20 次后开始返回图片验证码或滑块参数。原因站点对请求频率做了统计触发阈值后强制人机校验。用单线程高频直连最容易触发和用什么 Header 关系不大。解决拆分成“列表页慢速抓取 详情页并发受控”。列表页频率压到每秒 1 次详情页并发上限设 3。真遇到验证码先做频率退避而不是立刻上打码平台——大多数情况降频后验证码会自动消失。用验证码识别是最后的兜底手段不是第一选择。5.3 调试器反调让人没法打开 DevTools现象打开开发者工具准备调试 qq-m 页面的加密参数时页面立刻断住点击恢复又反复断根本没法看请求。原因页面里针对调试器做了反调典型实现是死循环 debugger或者检测到 DevTools 打开就重定向。解决在资源包里先看它的 JS 文件是否被本地替换过。常见做法是用抓包工具重写 JS 响应把 debugger 关键字替换成空字符串再配合断点定位。还有一个技巧是优先用浏览器内嵌的“空白标签页打开并注入 Hook”让被 Hook 的逻辑先跑起来从调用栈反查入口而不是直接面对调试器断点。5.4 签名比对不上同一个参数每次算出的结果不同现象用 Python 调用 sign.js 里的函数算法没改算出来的签名和服务端返回的签名对比不上或者连续两次结果不一致。原因签名算法里带了时间戳和随机数。排查时把同一个输入参数重复执行两次如果结果不同说明算法依赖了 Date.now() 或 Math.random()。这两个变量在 execjs 环境里和浏览器端取到的值不一样。解决看 JS 代码里有没有自执行函数先取了时间戳有的话把时间戳改成固定值再验证算法本身对不对。资源包里的 JS 逆向脚本大多留了调试入口直接把 debug 参数打开能看到传入签名字段和排除字段的合并逻辑。排查签名用的是固定值回归不是直接抓线上包对比——线上包时间戳每秒都在变对不上是正常的。6. 多站点联调与验收用三张表确保七个站不漏采资源包改完、单站跑通之后剩下的工作是联调和验收。七个站一起跑最怕修好一个坏了另一个。我习惯用一张指标表做验收把每个站点的运行结果量化。站点列表页抓取量详情字段数失败率平均耗时whggzy12080.8%12.3sqiming200151.2%18.6smohurd80120.5%9.8sendata50100.0%6.4s168860203.0%25.1scinfo4092.1%5.7sqq-m7571.1%8.2s这张表的三个指标分别对应列表页抓取量是否达标、详情字段是否齐全、失败率和耗时是否可接受。每个站点跑完输出一行验收时看整体趋势就够了。验证签名稳定性还有一个固定取值回归脚本# scripts/validate_sign.py fixed_time 1670000000000 # 先固定时间戳 fixed_params {keyword: python, page: 1} # 连续调用三次确认签名结果稳定 for _ in range(3): sign runner.sign(fixed_params, fixed_time) print(sign)逻辑说明固定时间戳和参数后签名结果应该完全一致不一致说明算法里有非确定性因素回到 5.4 排查。跑完这组验证再打开 run_all.py 做全量联跑观察各站日志里有没有新增异常。多站点采集最怕黑匣子每次改动都把指标表重新生成一张和上一次的结果对比改动是否引入回归。从那以后我每次给项目做爬虫实验都强制把“签名稳定、指标表更新、全量联跑”这三步走完才敢说做完。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑