尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
企查查爬虫实战:破解key、value与x-pid动态签名机制
说点实在的企查查这类的爬虫技术难点从来不在“怎么发请求”而在“怎么让请求看起来像真的”。我一开始照着网上教程套requests结果被一串key、value、x-pid拦得死死的返回的不是{code:4001,msg:请求非法}就是干脆空数据。后来花了两天时间把前端JS翻了个底朝天才把这三个参数的关系理清楚。这篇就把整个分析过程和实操思路写透给后来人省点时间。先明确一点本文讨论的是数据采集技术原理与前端反爬机制分析所有内容仅供学习交流实际使用请遵守目标站点的Robots协议和相关法律法规别拿去做违法乱纪的事。1. 企查查反爬体系拆解为什么偏偏是这三个参数1.1 前端请求链路先看明白企查查的Web端数据加载走的不是传统的服务端渲染而是典型的SPA单页应用架构。你在页面上点“查一下”浏览器不会刷新页面而是由JS动态构造一个XHR请求打到后端接口拿到JSON数据后再用前端框架渲染成表格。这就意味着所有关键数据都存在几个固定的API接口里比如企业列表、工商信息、股东信息这些。理论上只要找到这些接口直接请求就能拿到数据。但难点在于请求并不是裸奔的——每次请求都得带上几个动态生成的参数后端会先校验这些参数校验不过直接拒绝响应压根不给你数据。我把完整的请求拆开看过去掉Cookie和UA这些常规字段后真正决定“活不活”的参数就三样URL里的key查询参数请求体里的value表单字段Headers里的x-pid自定义头这三个参数是成套出现的缺一个或者算错了后端直接不认。早期企查查的反爬还没这么严有些教程里的代码写死一个固定key也能跑一两天现在基本不可能了因为这三个参数几乎每次请求都在变。1.2 key、value、x-pid在请求里分别扮演什么角色先说结论key是“门票”value是“请求内容”x-pid是“会话标识”。但三者之间不是独立的而是由同一套签名逻辑生成互相咬合。key通常出现在查询参数里看起来像一长串URL编码后的字符串里面有 base64 的痕迹解码后能看到timestamp时间戳、nonce随机数、x-service服务名之类的字段。它的作用是告诉后端“我这个请求是合法的”本质是一个签名凭证。value这个字段最坑。它在POST请求体里表面看是请求的业务参数比如你要查的企业名称、页码但实际经过了一层加密或编码不是明文。直接往里塞{name:某某公司,page:1}是没用的后端解析出来是个空对象。x-pid:这是一个自定义请求头后端用它来做会话维度的校验。但采集时x-pid是动态生成的还跟Cookie里的某些值关联。如果只带固定的x-pid请求频率一高就会被封。我踩过最大的坑就是只盯住了key以为把它搞定就万事大吉结果x-pid没过请求照样被拒。这三者必须作为一个整体去模拟不能拆开来单独处理。2. 核心参数来源定位怎么把“key值未知”变成已知2.1 抓包工具的正确使用姿势很多新手上来就用Fiddler或Charles结果抓到的全是乱码或HTTPS证书报错搞了半天连请求都看到不完整的。我的建议是直接用Chrome DevTools省事且直观。打开浏览器的开发者工具切到Network面板勾选Preserve log保留日志因为SPA页面会在切换路由时清空网络记录。然后在页面上触发一次查询比如搜索一个企业名称找到对应的XHR请求。这里有个筛选技巧不要一个一个翻直接在Filter框里输入api或/api/企查查的接口路径里通常带这个关键字。我通常还会先清空一次日志再触发操作这样留下来的请求基本都是本次操作产生的定位准确。选中那条关键的XHR请求后重点看三块Headers里的Request Headers看有没有x-pid或类似的非标准头。Payload或Request看提交的Form Data里有没有key、value。Query String Parameters看URL尾巴上跟的参数名是什么。第一次抓的时候大概率会看到key那一长串字符而value在Payload里显示为一段被转义的字符串。到了这一步很多人会陷入一个误区直接把这段抓到的key复制到代码里用。千万别这么干。我试过当时请求是成功了但半小时后突然大面积失效原因就是服务端会校验时间戳和nonce的时效性旧的key过几分钟就作废了。所以Copy下来只能用来临时验证代码逻辑不能作为长期方案。2.2 从URL到Headers逐步锁定生成位置确定参数名之后核心任务就是找到这些值是在哪段JS里生成的。第一步在DevTools的Sources面板里按CtrlShiftF全局搜索输入x-pid或x_pid。注意有的代码会写headers[x-pid] xxx有的会写headers[xPid]大小写和下划线都可能不一样多试几种写法。如果运气好直接就能搜到赋值语句比如headers[x-pid] getPid();然后跟进去找getPid这个函数的定义。但企查查这种体量的站点前端代码是打包压缩过的文件名可能叫app.8a9f3c.js一打开全是1行几百KB的压缩代码。直接找函数名基本不可能因为压缩后变量名都被替换成a、b、c之类的短名字了。这时候就得换思路用接口关键词回溯。在DevTools里点开那一条API请求在Initiator发起者面板里能看到调用链。它展示了是哪个JS文件、哪个函数发起了这个XHR。点进去之后在对应的代码位置打断点刷新页面重新触发请求就能在调用栈里看到参数是怎么一步步组装出来的。这一步是整个逆向的核心也是最费时间的。2.3 断点调试与调用栈分析的关键细节断点调试看起来高大上实际操作起来有几个细节需要注意。第一个细节要在发起请求之前打断点而不是在请求完成之后。因为等数据返回了函数的调用栈已经结束了。做法是在Initiator里找到发请求的那一行点行号设置断点然后重新触发页面操作比如再搜一次企业。第二个细节看清作用域里有什么。断点停下来后右侧的Scope面板会列出当前作用域的所有变量。你重点找名字里带key、sign、token、pid、timestamp、nonce的变量。如果某个变量值跟抓包抓到的key一模一样那它就是你要找的目标。第三个细节用Watch面板跟踪变量变化。在Watch里添加/key/这样的正则表达式就能实时看到跟key相关的所有变量的变化过程省得手动翻作用域列表。我记得当时就是从断点位置往上推了两层调用栈发现key和x-pid是在同一个函数里生成的——先算一个加密串然后一部分塞进URL一部分塞进Headers一部分塞进请求体。这就解释了为什么三个参数必须配套使用因为它们压根就是一条生产线上的产品。3. 解密思路动态key与value背后的加密逻辑3.1 加密参数的前世今生从公开的逆向资料和我自己分析的结果来看企查查这一类商查平台的签名机制基本都遵循同一个套路前端收集业务参数查询条件、页码、时间戳、随机数把参数按约定的顺序拼接成字符串经过某种摘要或加密算法常见的有MD5、SHA系列、AES或者自定义的Base64变体再把加密结果加上原参数一起做编码生成最终请求串后端拿到后先检查时间戳是不是在合理窗口内再按同样的算法重新计算一遍比对一致才放行为什么要这么设计后端接口如果裸奔任何人都能无限刷数据服务器压力大数据也容易被批量搬走。加一层动态签名后起码能把门槛提起来——你不能直接在浏览器里改个页码就翻页还得带上每次都变的签名。key和value的差异在于载体不同。key通常承载的是“签名时间戳”这类元信息value承载的是“实际的业务数据”加了一层编码。有些版本里二者还会互相引用比如key里带了一个加密因子value用这个因子加密后端校验时先解出因子再解value甭提多绕。3.2 常见混淆手段与识别特征为了不让逆向的人太轻松前端JS还会做混淆。识别混淆方式是决定后续工作量的关键。先说最基础的变量名压缩。这个在工程打包时默认就做了基本没法避免。所有的getCompanyInfo都会变成a或b阅读性归零。应对办法是不要试图读源码而是靠断点看值。再常见一点的是字符串拼接。搜x-pid搜不到完整字符串因为源码里写的是var t x-; var h t pid;这种散装字符串是专门对付全局搜索的。对策是全局搜索时只搜一部分比如搜pid或x-有时候甚至要搜URL编码后的写法。还有一类是加密函数特征。比如你看到JS里调用了md5()、sha256()、AES.encrypt那基本能确认签名用的算法。但如果代码里写的是function S(a){ return btoa(unescape(encodeURIComponent(a))) }那可能是自定义的Base64处理。我强烈建议与其硬啃混淆代码不如动态调试。动态调试的本质是让浏览器替你执行JS你只需要在下游观察变量的值比静态分析效率高一倍不止。3.3 模拟生成时的参数计算与组装当你通过调试摸清了生成逻辑下一步就是把它翻译成Python代码。这里我不放完整的逆向代码毕竟每个版本都在变直接抄没有意义但可以把共性的组装过程讲清楚。一般在代码里最终生成出来的key长这样示例结构keyeyJ0aW1lc3RhbXAiOiIxNzM2MDAwMDAwIiwibm9uY2UiOiI4YzY3Y2I...这串字符你看着眼熟吗去掉URL编码后开头是eyJ这是Base64编码JSON的固定前缀因为{的Base64就是eyJ。所以第一步永远是解码看看里面到底是什么。在Python里可以这样快速验证import base64 raw base64.b64decode(key) print(raw)如果解出来是{timestamp: 1736000000, nonce: xxx, x-service: yyy}这种结构恭喜你整条链路就豁然开朗了——你只需要在Python里重新构造这个JSON再用同样的算法算出后面那截签名即可。value的处理也类似它可能是把完整业务参数JSON做了一次编码也可能是在业务参数外又包了一层签名。判断方法很简单把value解出来看看里面是不是包含了你输入的企业名称如果包含说明只是编码如果不包含、是一段乱码说明是加密。x-pid这个东西比较特殊。从经验看它更像是一个会话级标识跟Cookie、设备指纹、时间戳都有关系。有些版本里它就是把当前时间反转、加密一下得到一个类似pid的字符串。有些版本里它会跟服务器下发的某个Cookie值联动请求前要先GET一次首页拿种子。我的建议是如果断点里能看到独立的getPid()函数尽量把它整个调用过程理清别只抄返回值。提示逆向分析一个动态参数时最快的路径不是通读全部源码而是找到“生成点 - 赋值点 - 传输点”这条最短链路。其余代码哪怕看不懂也不影响最终落地。4. 实操过程从抓包到跑通一次完整请求4.1 第一步先用浏览器手动复现在写任何代码之前先在浏览器里把请求完整复现一遍。这一步的目的不是拿数据而是确认“参数的有效性”和“请求的顺序”。我习惯这样操作打开无痕窗口访问企查查首页。打开DevTools切Network勾Preserve log。手动搜索一个公司名称找到XHR请求。把请求的URL、Headers、Payload全部复制下来。关掉页面重新打开无痕窗口再搜一次。注意无痕窗口这个细节。因为普通窗口会复用之前的Cookie和缓存第二次请求可能已经带着某些隐式参数了。用无痕窗口可以确保每次都是全新状态便于对比两次请求里哪些参数是稳定的、哪些是动态的。我对比之后发现Cookie里的某些字段每次都不一样x-pid每次也不一样而key和value每次也都在变。这说明整条链路完全是动态的任何一个写死的方案都撑不过一天。4.2 第二步用Python会话模拟请求确认逻辑后用Python的requests库搭建一个会话。核心要点是用同一个Session对象发请求让它自动管理Cookie。基础框架长这样import requests session requests.Session() # 1. 先访问首页获取初始Cookie和种子值 resp session.get(https://www.qcc.com/, headersbase_headers) seed extract_seed(resp.cookies) # 2. 根据种子和当前时间生成key、value、x-pid key generate_key(seed) value generate_value({name: 某企业, page: 1}) pid generate_pid(seed) # 3. 组装请求头和数据 headers { User-Agent: ..., x-pid: pid, Referer: https://www.qcc.com/search?key某企业, } data { key: key, value: value, } # 4. 发起请求 resp session.post(https://www.qcc.com/api/xxx/search, headersheaders, datadata) print(resp.json())这里有几个容易错的地方挨个说User-Agent必须跟浏览器一致有些反爬会校验UA的版本和浏览器指纹。Referer不能丢。SPA页面发起请求时Referer是当前页面的URL后端可能会校验这个来源是否合理。请求体里的数据格式要看清。企查查有些接口用application/json有些用application/x-www-form-urlencoded搞错了后端解析不了就报错。至于generate_key、generate_value、generate_pid这三个函数的内容就是你在第3节逆向出来的逻辑。如果逆向不完整可以先写一个“临时函数”把从浏览器里抓到的值硬编码进去先跑通请求再逐步替换成动态生成。先连通再完善这是我在所有爬虫项目里坚持的原则。4.3 第三步处理Cookie、会话保持和频率控制跑通一次请求不难难的是跑通了还能持续跑。这里最大的坎就是Cookie和频率控制。我第一次实现动态生成参数后能正常拿数据了但跑到第30次请求时突然返回要求验证。排查后发现是频率太猛同一个Session在几秒内连发了30次请求风控直接盯上了。解决方案分两个层面代码层面在每次请求间隔加随机延时比如time.sleep(random.uniform(2, 4))。注意是随机延时不是固定延时。固定延时会被统计规律识别出来随机延时至少能干扰一下。策略层面不要让一个Session工作太久跑一段时间就换一组Cookie。我当时写了一个简单的Cookie池每跑50次请求就重新开个Session相当于换了一个“新用户”的身份。关于IP这块我不展开但自己测试的时候建议谨慎别把个人常用IP玩进风控名单。真要大批量跑优先考虑合规的代理方案并且控制并发量。4.4 数据解析xpath与text函数提取关键字段数据拿到手之后就是解析环节。企查查的接口返回的是JSON按理说直接解析JSON就够了但有些场景下你会拿到HTML片段或者某个字段里嵌套了HTML表格这时xpath和text()函数就派上用场了。Python里用lxml库做解析示例from lxml import etree html etree.HTML(response_text) names html.xpath(//a[classcompany-name]/text())这里text()函数的作用是提取标签内的文本内容。需要注意text()匹配的是“直接子文本节点”如果标签内部还嵌套了子标签text()可能拿到空值或只拿到部分内容。这时候用string(.)更保险names html.xpath(//a[classcompany-name]/string(.))string(.)返回当前节点下所有文本拼接后的结果不会因为嵌套标签而丢内容。金融、风控、市场调研场景里很多数据其实是混合结构的——JSON里嵌一段HTMLHTML里嵌一串JSON。这时候要灵活切换解析方式不能只认准一种工具。我一般会写一个辅助函数先判断字符串是JSON还是HTML再走不同的解析分支。5. 常见问题与排查技巧实录5.1 高频报错速查表实操过程中我整理过一份高频问题表直接贴出来现象可能原因排查方向返回{code:4001,msg:请求非法}签名校验失败key或x-pid算错了重新断点确认生成逻辑核对时间戳是否正确返回内容带了验证码链接频率过高触发风控降频、换Session降低单个会话的请求数请求直接超时或连接重置IP被限换网络环境或调整请求间隔返回空数组但状态码200value里的业务参数编码有问题解码value确认业务参数是否被正确包进去单独请求接口能通代码里不通Header字段缺失或Cookie不带检查Referer、UA、Cookie的传递链路key解出来是乱码JSON结构都没有解压/编码层级没剥完多尝试几次URL解码、Base64解码观察是否有嵌套编码遇到4001的时候别急着改代码。先在浏览器里把当前请求的key复制出来拿到Python里跑一遍同样的请求如果能通说明是签名生成部分有BUG如果也不能通说明签名逻辑本身已经变了得重新断点分析。5.2 反爬升级的应对策略企查查这类平台的反爬升级很勤快。今天能用的方案下周可能就废了。我总结过几条应对经验做好断点调试的功课别依赖网上教程。教程里的代码只是某个时间点的快照站点一升级就失效而断点调试能力是通用的不管参数怎么变都能重新定位。把生成逻辑模块化。在代码里把key生成、value生成、pid生成、请求发送拆成独立函数每次更新只需要改对应函数不用全盘重写。保留历史请求样本。我习惯每次遇到异常先把浏览器里的完整请求URL、Headers、Body存成一个文本文件方便跟代码里的请求对比。很多BUG一眼就能看出来——字段名大小写、多了个空格、body格式错了都是对比才能发现的。还有一点如果目标数据量很大建议优先评估目标平台是否提供官方API或数据服务。很多商查平台现在都有付费数据接口稳定性远高于自己去逆向。爬虫能解决“能不能拿到”的问题但拿不到“稳定可持续”的保障。5.3 合规与稳定性的平衡最后说点容易被忽视但很重要的东西——合规和自我约束。爬虫这个东西技术本身是中性的但用起来边界特别清楚。我在做数据采集项目时给自己定了几条硬规矩只采集公开可见的数据不碰任何需要登录后才能看到的非公开信息。控制访问频率不给目标服务器造成压力。我的标准是单IP并发不超过3请求间隔不低于2秒。不对目标平台的核心业务比如账号注册、支付流程发起任何自动化请求。数据仅用于个人学习或合法业务分析不转卖、不公开。这几条规矩看起来保守但能保证自己在安全线以内活动。毕竟爬虫的本质是“访问Web服务”如果访问方式越界了性质就变了。最后分享一点个人经验折腾企查查这套参数下来我最大的体会有两点。第一解密类的工作80%的时间都花在“定位”上只有20%花在“实现”上。定位做扎实了后面的代码都是体力活定位偷懒了后面全是返工。第二不要迷信某一个固定方案的“永久有效”。任何反爬对抗都是动态博弈今天你解开了key和value明天人家可能就换了算法、加了验证码。保持能快速定位问题、快速重新逆向的能力才是这类项目里真正值钱的东西。如果你也正在被x-pid折磨我的建议是先把浏览器断点玩熟搞清楚这三个参数怎么生成的再谈写代码的事。工具和库都是其次的逆向思路才是核心。希望这篇记录能帮你少踩几个坑。
RELATED

相关推荐

小波分解+BP神经网络风电功率预测实战指南

小波分解+BP神经网络风电功率预测实战指南

简介:本资源是一份面向电力系统、新能源预测及人工智能应用方向的科研与工程实践者的技术文档,聚焦风电功率不确定性带来的电网调度难题,提出融合小波分析与BP神经网络的高精度短期预测方法。文档系统阐述了小波分解(DB4四层&…

📅 2026/10/9 10:54:29
Vue+ECharts动态地图实战:数据驱动着色与下钻联动

Vue+ECharts动态地图实战:数据驱动着色与下钻联动

1. 项目背景与核心需求拆解1.1 为什么要在Vue项目里做动态地图做过数据大屏或者后台管理系统的朋友应该都有体会,静态地图早就满足不了业务需求了。所谓动态地图,核心诉求无非这么几类:地图区域能根据数据变化自动着色、点击某个省份能下钻到…

📅 2026/10/9 10:54:29
浏览器插件开发避坑指南:Manifest V3实战通信与状态管理

浏览器插件开发避坑指南:Manifest V3实战通信与状态管理

1. 这不是教程,是我在三年里踩过27次坑后整理的浏览器插件开发实录“浏览器插件开发终极指南:从入门到精通”——这个标题听起来像极了那种点开前信心满满、读完后怀疑人生的文档。我第一次写插件时,也是被“5分钟上手”“一行代码搞定”这类…

📅 2026/10/9 10:54:29
MORE NEWS

更多资讯

📰

Canvas仿真烟花特效:物理建模、渲染与性能优化实战

简介:仿真烟花主题的前端特效源码包,适合网页开发者、动画爱好者用于学习参考或直接嵌入页面,实现逼真绚丽的烟花绽放效果。包内仅4个文件,包含1个可直接运行的HTML入口文件与3张辅助背景图片(城市夜景、月亮等&#x…

📰

Windows服务器部署Oracle 19c:从安装包到静默安装的完整路线

简介:这是Oracle Database 19c在Windows x64平台上的完整安装资源包,面向需要本地部署、测试或学习该版本数据库的DBA、开发人员与运维工程师,用于解决企业级数据库安装与初始配置难题。压缩包共约2000个文件,以jar、xml、dll、ex…

📰

2026继续教育论文降AI率工具实测:9款主流工具测评与避坑指南

2026年这个节点,继续教育圈的写作群里,大家讨论最多的已经不是选题,不是文献,而是检测报告里那行“AIGC疑似占比”。我帮不少学员看过初稿,也亲眼见过有人辛辛苦苦写完的东西被误判成AI生成。这几年降AI率工具的需求一…

📰

数据分析面试SQL题汇总:高频考点与易错点全解析

简介:一份面向数据分析岗位的SQL面试题汇总文档,适合求职者、转行者以及初级数据分析师按需复习。文档围绕建表、插入数据、排序、连接、分组、聚合函数、日期操作等高频考点展开,通过两道典型面试题完整演示了从数据加载到计算活跃度、次日留…

📰

iOS一键编译FFmpeg全家桶:交叉编译与架构合并实战

简介:面向 iOS 平台音视频开发者的 FFmpeg 交叉编译辅助工具,将 ffmpeg、x264、fdk-aac、lame 四大开源库的源码下载、参数配置与编译流程整合为一套 Shell 脚本,适合需要在模拟器或真机环境中快速获得自定义 iOS 库的中高级开发者。资源共 4…

📰

GPU 顶点瓶颈 vs 片元 Overdraw:通过修改视口分辨率快速定位渲染管线短板

在大型 3D 游戏性能攻坚现场,当渲染主线程排除了 CPU 提交阻塞、确认瓶颈位于 GPU 侧(GPU Bound)时,开发者面临的下一个十字路口往往是:当前掉帧到底是由前端几何顶点处理与细分面数过多引起的(Vertex/Geom…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬