1. 项目概述:为什么“看得到”不等于“拿得到”?Network 面板才是爬虫人的真·眼
你有没有遇到过这种情况:网页上明明白白展示着商品价格、用户评论、实时销量,右键“查看网页源代码”却只看到一堆空 div 和加载中提示?F12 打开开发者工具,切到 Elements 标签页,层层展开 DOM,结果关键数据根本不在里面——它藏在某个看不见的角落,等你主动去“听”、去“抓”、去“解码”。这就是现代 Web 的常态:前端渲染(SPA)、接口驱动、动态加载。页面是“画”出来的,不是“写”出来的。而 Network 面板,就是那个能让你听见页面心跳、看清数据脉搏的听诊器。
这个标题里的「基于开发者工具 Network 的数据抓包技术」,说的不是黑产意义上的“窃取”,而是正向工程视角下的“逆向理解”——它是一套标准化、可复现、完全合法的前端通信观测方法论。核心关键词就三个:Python爬虫、开发者工具、Network面板。它解决的是爬虫工程师最常卡壳的“第一公里”问题:当目标网站不再用传统 HTML 直接吐数据,而是靠 JavaScript 调用 API 拉取 JSON,你得先知道这个 API 是什么、在哪调、带什么参数、返回什么结构,Python 才能精准模拟。否则,你写的不是爬虫,是碰运气的盲人摸象。
我带过的几个刚转行做数据采集的新人,90% 的挫败感都来自这里:他们能熟练写requests.get(),能处理BeautifulSoup解析,但一遇到 Vue/React 构建的后台管理系统、电商商品详情页、股票行情面板,就彻底懵了。不是代码不会写,是根本不知道该往哪个 URL 发请求。Network 抓包,就是把“猜”变成“看”,把“试错”变成“确认”。它不依赖反爬强度,不挑战服务器底线,纯粹是浏览器自身开放给开发者的调试能力。你用 Chrome 访问一个页面,它每发一个请求,Network 就记一笔;你点一下筛选按钮,它就多发几个请求;你拖动滚动条加载更多,它就再记几笔。这些记录,就是你后续用 Python 复刻行为的完整操作日志。
适合谁学?三类人必须掌握:一是刚入门想摆脱“静态页面爬虫”局限的新手,这是你进阶的必经跳板;二是工作中频繁对接内部系统、管理后台、BI 看板的业务型爬虫使用者,这类页面几乎 100% 依赖接口;三是需要做竞品数据监测、舆情分析、价格追踪的运营或产品同学,你不需要写完整爬虫,但必须能快速定位数据源头。它不考算法,不拼框架,只考你是否建立了“前后端分离”的底层认知。今天这篇,我就带你从打开 Chrome 开始,手把手拆解 Network 面板里每一列的含义、每一个过滤器的用法、每一种请求类型的识别逻辑,并最终落地到 Python 代码——不是简单复制粘贴,而是让你真正理解:为什么这个请求要加Referer,为什么那个接口返回 403,为什么X-Requested-With头不能少。这才是能让你在真实项目里稳住不翻车的核心能力。
2. 内容整体设计与思路拆解:从“看见请求”到“复现请求”,中间隔着五个认知断层
很多人以为 Network 抓包 = 点开 DevTools → 切到 Network → 刷新页面 → 找个.json文件 → 复制 URL →requests.get()。这就像学开车只练挂挡,没练离合和油门配合。实际项目中,这种“一眼定乾坤”的操作成功率不到 30%。真正可靠的抓包流程,是一套分阶段、有策略、带验证的闭环设计。我把它拆成五个不可跳过的环节,每个环节都对应一个常见的认知断层,跨不过去,后面全白搭。
2.1 断层一:混淆“请求发起时机”与“数据渲染时机”
新手常犯的错误是:页面加载完,数据也显示出来了,就认为所有请求都结束了。但现实是,很多数据是“懒加载”的。比如一个商品详情页,首屏只加载基础信息,用户向下滚动时才触发评论列表请求,点击“规格参数”标签页才拉取 SKU 数据。如果你只刷新一次就停止观察,就会漏掉关键接口。正确做法是:模拟真实用户行为路径。先清空 Network 面板(Ctrl+R 或点击左上角垃圾桶图标),然后手动执行完整操作链:打开页面 → 等待首屏加载完成 → 滚动到底部 → 点击各个 Tab → 输入搜索词并回车 → 点击“加载更多”。每一步都要盯着 Network 面板,看新请求是否涌出。我习惯用Filter输入fetch或xhr,再按Ctrl+Shift+R强制刷新(跳过缓存),确保捕获所有动态请求。
2.2 断层二:忽略“请求头(Headers)的上下文依赖”
找到一个看起来很美的https://api.xxx.com/v2/products?sku=12345,兴冲冲丢进 Python 里跑,结果返回403 Forbidden或{"code":401,"msg":"Unauthorized"}。问题往往不出在 URL,而在 Headers。现代接口普遍校验Referer(告诉服务器你从哪来)、User-Agent(标识客户端类型)、Cookie(维持登录态)、甚至X-Requested-With(声明是 AJAX 请求)。Network 面板里,点击任意请求 → 右侧切换到Headers标签页 → 往下拉,Request Headers区域就是你要抄的作业。特别注意Cookie字段,它通常是一长串键值对,直接复制整行内容,别漏掉分号。Referer更关键,如果目标网站做了 Referer 白名单,你用 Python 直接访问却没带这个头,服务器会直接拒收。这不是防爬,是标准 Web 安全实践。
2.3 断层三:误判“请求方法(Method)与参数载体”
看到POST请求就默认参数在 Body 里,看到GET就只看 URL Query 参数,这是典型的经验主义陷阱。实际中,POST请求的参数可能藏在Form Data(表单提交)、Request Payload(JSON 格式)、甚至Query String Parameters(URL 里带参数)里。Network 面板里,Headers标签下General部分会明确标出 Method,而下方Request Payload或Form Data标签页则展示实际发送的数据。比如一个搜索接口,URL 是GET /search,但真正决定搜什么的keyword参数,却放在Request Payload的 JSON 体里(这其实是非标准用法,但真实存在)。Python 复现时,GET请求用params参数传 Query,POST请求则要根据 Network 显示的格式,选择data(字典或字符串)或json(字典,自动序列化并设Content-Type: application/json)。
2.4 断层四:忽视“响应数据(Response)的结构真实性”
点击Preview标签页看到漂亮的 JSON 树状结构,就以为response.json()能直接解析。但Preview是浏览器帮你格式化后的视图,真实响应可能是压缩的(Gzip)、编码的(Base64)、或加密的(AES)。更常见的是,Response标签页显示的是原始字节流,而Preview是解析后的内容。如果Preview是乱码或空白,说明响应未被正确解码。此时要检查Headers里的Content-Encoding(如gzip)和Content-Type(如application/json;charset=utf-8)。Python 中,requests库默认处理 gzip 解压,但charset错误会导致中文乱码,需手动指定response.encoding = 'utf-8'。另外,有些接口返回的不是纯 JSON,而是JSONP(带函数包裹的 JSON),Preview会显示为 JS 代码,这时你需要用正则提取括号内的 JSON 字符串。
2.5 断层五:缺失“请求链路(Initiator)的因果关系”
Network 面板左侧Name列旁有个小图标,写着initiator,点击它能看到这个请求是由哪个 JS 文件、第几行代码发起的。这看似鸡肋,实则是破译复杂逻辑的钥匙。比如你发现一个关键数据接口,但它的Referer是https://xxx.com/dashboard,而你抓包时是在https://xxx.com/product/123页面发起的。这就说明,这个请求不是页面加载时自动发的,而是由某个 JS 脚本在特定条件下触发的。点击initiator,定位到 JS 文件,再在Sources面板里搜索接口名或参数名,往往能找到生成请求 URL 或签名的逻辑。这步虽不总需 Python 实现,但它决定了你是“照搬”还是“理解后重构”。对于需要登录态、时间戳、签名参数的接口,光抄 Headers 是不够的,你得读懂 JS 里的计算逻辑。
这套五步设计,本质是把 Network 从“记录仪”升级为“分析仪”。它不追求一次抓全,而强调分阶段验证:先确认请求存在(断层一),再确认请求能发(断层二、三),接着确认响应能读(断层四),最后确认逻辑可复(断层五)。我在某次为某高校实验室做课程数据采集时,就卡在断层五——一个成绩查询接口要求sign参数,是 JS 用当前时间戳和固定密钥 MD5 生成的。如果只抄 Headers,每次请求都会因 sign 过期失败。正是通过initiator定位到 JS,才还原出 Python 的签名算法。这五个断层,就是你从“会用”到“精通”的分水岭。
3. 核心细节解析与实操要点:Network 面板每一列、每一个按钮,都是你的线索
Network 面板界面看似简单,但每一列、每一个按钮、每一个标签页,都藏着解读网络通信的关键密码。新手常因忽略某个小图标或误读某列数值,导致抓包失败。下面我逐项拆解,结合真实场景说明其含义、易错点和实操技巧,全是我在上百个项目里踩坑后总结的硬核细节。
3.1 Name 列:不只是文件名,它是请求的“身份ID”
Name列显示的是请求的资源名称,通常是 URL 的最后一段(如list.json,user_info,v1/orders)。但它的价值远不止于此。首先,它是你快速筛选的依据。比如你想找用户数据,直接在顶部Filter框输入user,所有含 user 的 Name 就高亮了。其次,它隐含了资源类型。.js结尾大概率是脚本,.css是样式,.png/.jpg是图片,.json或无后缀但路径含/api/的,基本是数据接口。但要注意陷阱:有些接口故意伪装成图片,比如https://xxx.com/get_data?token=abc.png,Name 显示abc.png,但响应却是 JSON。这时要结合Type列和Response内容判断。
提示:右键点击
Name列的任意请求,选择Copy→Copy link address,可直接复制完整 URL。但注意,这仅适用于 GET 请求。对于 POST,URL 不包含 Body 数据,复制了也没用。
3.2 Status 列:状态码不是冷冰冰的数字,是服务器的“情绪反馈”
Status列显示 HTTP 状态码,如200,304,404,500。新手只关注200,但其他码同样重要。304 Not Modified表示资源未变,走的是浏览器缓存,这个请求的响应体是空的,你无法从中获取新数据,但它告诉你:这个接口支持缓存,后续 Python 请求可以加If-None-Match头优化性能。401 Unauthorized和403 Forbidden常被混为一谈,其实区别很大:401是“你没登录”,缺的是认证凭证(如 Cookie 或 Token);403是“你登录了但没权限”,可能是 Referer 校验失败、IP 被限、或账号等级不足。遇到403,第一反应不是换 IP,而是回头检查Referer和Origin头是否准确复制。500 Internal Server Error则说明服务器崩了,和你无关,等会儿再试。
3.3 Type 列:识别数据接口的“黄金指标”
Type列比Name更可靠地指示资源类型。重点关注xhr(XMLHttpRequest,传统 AJAX)、fetch(现代 Fetch API)、document(HTML 文档)、script(JS 文件)。数据接口几乎 100% 是xhr或fetch。document类型是页面主 HTML,script是 JS 文件,它们本身不返回业务数据,但可能包含生成接口 URL 的逻辑(回到断层五)。Type列还可能显示other,这通常意味着请求被浏览器拦截(如跨域失败)或类型未被识别,需点开查看详情。
3.4 Initiator 列:找到“幕后推手”的唯一路径
如前所述,Initiator显示请求的发起者。它有两种形式:一种是xxx.js:123,表示由xxx.js文件第 123 行发起;另一种是(index):1,表示由当前 HTML 页面的内联脚本发起。点击它,DevTools 会自动跳转到Sources面板对应位置。这是逆向 JS 逻辑的起点。实战中,我曾遇到一个电商比价接口,Name是price,Type是fetch,但Headers里没有明显签名。点击Initiator,跳转到main.js:876,发现一行代码:const sign = md5(timestamp + 'secret_key' + sku)。瞬间明白,timestamp是当前毫秒时间戳,sku是商品 ID,secret_key是写死的字符串。Python 里用hashlib.md5()两行就搞定。没有Initiator,你可能花几天都找不到这个 key。
3.5 Size 和 Time 列:性能瓶颈的“晴雨表”
Size显示响应体大小(不包括 Headers),Time显示从请求发出到接收完毕的总耗时。这两个值对爬虫至关重要。如果一个接口Size动辄 5MB,Time超过 3 秒,说明它可能返回了大量冗余数据(如全量商品库),你需要在 Python 里用params加limit=20等参数精简。Time长还可能是网络抖动,但若多个请求都慢,则要考虑目标服务器限速,Python 里就得加time.sleep(1)控制频率。另外,Size为0且Status是200,说明响应体为空,可能是状态变更类接口(如点赞、收藏),你只需关注Status是否为200即可,不用解析响应。
3.6 Filter 和 Preserve log:让杂乱变有序的“控制台”
顶部Filter是 Network 的灵魂。除了按Name关键词过滤,还能用高级语法:
domain:xxx.com:只显示指定域名的请求;status-code:200:只显示 200 响应;larger-than:10k:只显示大于 10KB 的响应(快速定位大数据接口);method:POST:只显示 POST 请求。
Preserve log(保留日志)复选框必须勾选!否则页面跳转或刷新后,之前的请求记录全丢。这是新手最大失误点——辛辛苦苦抓了一堆请求,点个链接就没了。勾上它,日志会一直保留,直到你手动清空。
3.7 Response 与 Preview 标签页:真相与幻象的边界
Response是原始字节流,Preview是浏览器解析后的视图。两者不一致时,以Response为准。比如Preview显示 JSON,但Response是乱码,说明编码错误;Preview是空白,Response有内容,说明是二进制(如 PDF)或未识别格式。此时要检查Headers里的Content-Type。如果Content-Type是application/octet-stream,那它就是二进制流,Python 里要用response.content(bytes)而非response.text(str)来处理。
注意:
Preview对 JSON 支持最好,对 XML、HTML 也能格式化,但对自定义二进制协议完全无效。别迷信Preview,学会看Response是基本功。
4. 实操过程与核心环节实现:从抓包到 Python 复现,一个电商商品详情页的完整案例
理论讲完,现在用一个真实、完整、可复现的案例,带你走一遍从打开浏览器到跑通 Python 代码的全流程。目标:抓取某电商平台(我们称其为“星购网”)一个商品详情页的实时库存、价格和用户评分。这个页面是典型的 React SPA,Elements 里只有骨架,所有数据由接口动态加载。我们将严格遵循前文的五步设计,不跳步、不取巧。
4.1 步骤一:环境准备与初始抓包
首先,确保 Chrome 是最新版(旧版 Network 功能可能受限)。打开 Chrome,按F12或Ctrl+Shift+I打开 DevTools。点击Network标签页。关键动作:
- 勾选
Preserve log(防止刷新丢失记录); - 点击左上角垃圾桶图标清空现有日志;
- 在
Filter框输入fetch(聚焦现代接口); - 按
Ctrl+Shift+R强制刷新(跳过缓存,确保抓到所有请求)。
然后,在地址栏输入一个商品详情页 URL,例如https://www.xinggou.com/product/10086。等待页面完全加载(底部状态栏不转圈,Network 面板无新请求涌入)。此时,面板里应该有几十上百条请求。我们的目标是找出库存、价格、评分对应的接口。
4.2 步骤二:精准定位目标接口
商品页上,库存通常显示为“仅剩 3 件”,价格是“¥299.00”,评分是“4.8 分(1256 条评价)”。我们用Filter逐个排查:
- 输入
stock或inventory,无结果; - 输入
price,出现一个https://api.xinggou.com/v3/product/price?pid=10086,Type是fetch,Status是200,点击它; - 切到
Response标签页,Preview显示{"price":"299.00","original_price":"399.00","currency":"CNY"}—— 成功!这就是价格接口。 - 同理,输入
score或review,找到https://api.xinggou.com/v3/product/review_summary?pid=10086,Response是{"score":4.8,"review_count":1256}—— 评分接口。 - 库存呢?输入
stock无果,试试quantity,出现https://api.xinggou.com/v3/product/inventory?sku=10086,Response是{"available_quantity":3,"total_quantity":100}—— 库存接口。
实操心得:接口名不总是直白的。
inventory比stock更常用,review_summary比score更规范。多尝试同义词,比死磕一个词高效得多。
4.3 步骤三:深度解析请求细节(Headers & Payload)
以价格接口https://api.xinggou.com/v3/product/price?pid=10086为例,点击它,详细分析:
Headers→General:Method 是GET,所以参数在 URL 里,pid=10086就是商品 ID。Headers→Request Headers:重点看以下几行:Accept: application/json, text/plain, */*Referer: https://www.xinggou.com/product/10086← 必须复制!User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...← 复制整行Cookie: sessionid=abc123; csrftoken=xyz789;← 整行复制,分号结尾X-Requested-With: XMLHttpRequest← 常见 AJAX 标识,加上更稳妥
Response→Headers:Content-Type: application/json; charset=utf-8,说明响应是 UTF-8 编码的 JSON。
4.4 步骤四:Python 复现与验证
现在,把以上信息翻译成 Python 代码。我们用requests库,这是最轻量、最可控的选择。
import requests import time # 1. 构建请求头(Headers) headers = { "Accept": "application/json, text/plain, */*", "Referer": "https://www.xinggou.com/product/10086", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Cookie": "sessionid=abc123; csrftoken=xyz789;", "X-Requested-With": "XMLHttpRequest" } # 2. 发送 GET 请求 url = "https://api.xinggou.com/v3/product/price?pid=10086" try: response = requests.get(url, headers=headers, timeout=10) # 3. 检查状态码 if response.status_code == 200: # 4. 设置编码,避免中文乱码 response.encoding = 'utf-8' # 5. 解析 JSON data = response.json() print(f"商品价格: ¥{data['price']}") print(f"原价: ¥{data['original_price']}") else: print(f"请求失败,状态码: {response.status_code}") print(f"响应内容: {response.text[:200]}") # 打印前200字符,便于调试 except requests.exceptions.RequestException as e: print(f"网络请求异常: {e}")运行这段代码。如果返回200并打印出价格,恭喜,第一步成功!如果失败,根据错误信息回溯:
403:检查Referer是否精确匹配(注意末尾斜杠);401:检查Cookie是否过期(重新登录网站,再抓一次);Timeout:目标服务器响应慢,增大timeout值或加重试逻辑。
4.5 步骤五:封装通用函数与批量采集
单个商品没问题,但实际需求是采集 1000 个商品。我们需要封装一个健壮的函数:
def fetch_product_price(product_id: str, session: requests.Session) -> dict: """ 获取单个商品价格信息 :param product_id: 商品ID :param session: 复用的Session对象,自动管理Cookie :return: 包含价格信息的字典,失败返回空字典 """ url = f"https://api.xinggou.com/v3/product/price?pid={product_id}" headers = { "Accept": "application/json, text/plain, */*", "Referer": f"https://www.xinggou.com/product/{product_id}", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "X-Requested-With": "XMLHttpRequest" } try: response = session.get(url, headers=headers, timeout=10) if response.status_code == 200: response.encoding = 'utf-8' return response.json() else: print(f"获取商品 {product_id} 价格失败: {response.status_code}") return {} except Exception as e: print(f"获取商品 {product_id} 价格异常: {e}") return {} # 使用示例:批量采集 if __name__ == "__main__": # 创建 Session,复用连接和 Cookie session = requests.Session() # 先访问一次首页,获取初始 Cookie(很多网站需要) session.get("https://www.xinggou.com", timeout=10) product_ids = ["10086", "10087", "10088"] # 示例ID列表 for pid in product_ids: price_data = fetch_product_price(pid, session) if price_data: print(f"商品 {pid}: ¥{price_data.get('price', 'N/A')}") # 控制请求频率,避免被限 time.sleep(0.5)实操心得:用
requests.Session()是关键。它能自动管理Cookie,省去手动传递的麻烦。首次访问首页,是为了让 Session 获取网站的基础 Cookie(如csrftoken),这对后续接口至关重要。time.sleep(0.5)是温柔的节流,比暴力time.sleep(1)更高效,又比不加更安全。
5. 常见问题与排查技巧实录:那些让我熬夜到凌晨三点的 Bug
再完美的流程,也会遇到意料之外的状况。以下是我在真实项目中反复遭遇、并已形成肌肉记忆的 7 个高频问题,附带排查思路和终极解决方案。它们不是教科书里的“可能”,而是血泪教训。
5.1 问题一:Network 面板一片空白,刷新后啥也没有
现象:打开 Network,刷新页面,面板空空如也,连document请求都没有。
排查思路:
- 第一反应:检查
Preserve log是否勾选?没勾的话,刷新就清空了。 - 第二反应:看左上角
Recording按钮是否是红色(开启状态)?如果是灰色,点击它启动录制。 - 第三反应:检查是否启用了“禁用缓存”(Disable cache)?有时勾选了它,反而导致某些资源不加载。取消勾选再试。
- 终极方案:按
F12→Settings(齿轮图标)→Preferences→Network→ 确保Enable network logging已勾选。如果还不行,重启 Chrome。
5.2 问题二:找到了接口,但 Python 返回 403,Headers 也完全一样
现象:Referer、User-Agent、Cookie全部复制,curl命令测试也 403。
排查思路:
- 检查
Cookie是否过期。Network 抓包时,Cookie是当时有效的,但可能 5 分钟后就失效。解决方案:在 Python 里,不要硬编码 Cookie,而是用session.get()先访问登录页或首页,让requests自动管理。 - 检查
Origin头。有些网站(尤其 CORS 严格的)会校验Origin。Network 面板里Request Headers下可能没显示Origin,但服务器在后台检查。手动加上:"Origin": "https://www.xinggou.com"。 - 检查
Sec-Fetch-*系列头。Chrome 100+ 新增了Sec-Fetch-Site,Sec-Fetch-Mode等头,用于声明请求来源。虽然不是所有网站都校验,但Sec-Fetch-Site: same-origin和Sec-Fetch-Mode: cors是常见组合。复制它们。
5.3 问题三:Response Preview 是 JSON,但response.json()报JSONDecodeError
现象:Preview显示完美 JSON,response.text却是乱码或一堆\uXXXX。
排查思路:
response.text是str,response.content是bytes。先打印response.content[:100]看原始字节。如果开头是b'\x1f\x8b',说明是 Gzip 压缩,requests默认已解压,问题在编码。- 检查
response.encoding。requests有时会误判编码。强制设置:response.encoding = response.apparent_encoding(用 chardet 推测)或response.encoding = 'utf-8'。 - 如果
response.content是b'{"key":"\u4f60\u597d"}',这是 Unicode 转义,response.json()能正确解析,无需担心。乱码是指response.text显示为 `` 符号。
5.4 问题四:接口返回 200,但数据是空的或格式不对
现象:Status是 200,Response有内容,但Preview是空,或response.json()返回空字典。
排查思路:
- 检查
Content-Type。如果是text/html,说明服务器返回了错误页面(如 404 页面),但状态码没设对。用response.text查看原始 HTML。 - 检查是否需要登录态。即使
Cookie有,也可能sessionid过期。在浏览器里手动访问该接口 URL,看是否跳转到登录页。如果是,说明需要重新登录抓包。 - 检查参数。
pid=10086可能是无效 ID。在浏览器里修改 URL 为pid=10087,看是否返回正常数据。
5.5 问题五:POST 请求的 Form Data 怎么在 Python 里发?
现象:Network 显示Form Data,里面有username=test&password=123,但requests.post(url, data="username=test&password=123")不行。
解决方案:
data参数接受dict或str。传dict最安全:requests.post(url, data={"username": "test", "password": "123"})。requests会自动编码为application/x-www-form-urlencoded。- 如果
Form Data显示的是{"username":"test"}(JSON 格式),那它其实是Request Payload,不是Form Data。此时用json参数:requests.post(url, json={"username": "test"})。
5.6 问题六:如何抓取 WebSocket(ws/wss)消息?
现象:页面有实时聊天、股价推送,Network 面板里Type是websocket,但点开看不到消息内容。
解决方案:
- Network 面板只显示 WebSocket 连接建立(
101 Switching Protocols),不显示后续帧。要抓消息,需切换到WS(WebSocket)标签页(在 Network 同级)。 - 点击
WS标签页,找到连接,左侧Frames显示收发的消息。Data列就是消息体,可能是文本(Text)或二进制(Binary)。 - Python 复现需用
websocket-client库,不是requests。代码示例:from websocket import create_connection ws = create_connection("wss://api.xinggou.com/ws") ws.send('{"action":"subscribe","symbol":"AAPL"}') result = ws.recv() # 接收消息 print(result)
5.7 问题七:接口有加密参数(sign、token),怎么破解?
现象:URL 里有&sign=abc123×tamp=1700000000,且每次刷新都变。
排查思路:
- 第一步:用
Initiator定位到 JS 文件,搜索sign、md5、sha256、hmac等关键词。 - 第二步:在
Sources面板里,找到生成sign的函数,右键Add breakpoint,在浏览器里触发请求,代码会停住,你可以看到变量值。 - 第三步:如果 JS 混淆严重,用
Pretty print({} 按钮)格式化,再搜索。 - 终极方案:用
Selenium或Playwright启动真实浏览器,执行 JS 计算sign,再用requests发送。虽然慢,但 100% 可靠。
常见加密方式速查表:
加密方式 JS 常见函数 Python 对应库 备注 MD5 md5(str)hashlib.md5().hexdigest()最弱,已不推荐 SHA256 sha256(str)hashlib.sha256().hexdigest()常见 HMAC-SHA256 hmacSHA256(key, str)hmac.new(key, str, hashlib.sha256).hexdigest()需密钥,更安全 AES CryptoJS.AES.encrypt()pycryptodome复杂,需 IV 和密