ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

淘宝评论采集:接口签名逆向与Python实现详解

淘宝评论采集:接口签名逆向与Python实现详解 简介本资源是一套面向Python爬虫开发者与电商数据分析学习者的淘宝商品评论采集实战代码聚焦于突破淘宝前端加密含sign逆向的技术难点适用于商品舆情分析、竞品监控及用户行为研究等场景。压缩包共5个文件约227KB包含核心采集脚本.py、逆向关键逻辑的JavaScript实现.js、项目说明文档.md、示例截图.png及版本控制配置.gitignore结构精炼便于快速理解逆向流程与请求构造原理。已有162人下载学习适合具备基础HTTP协议与JavaScript调试能力的中级开发者参考。读者可直接复用采集逻辑掌握httpx异步请求、动态sign生成、评论分页解析及结构化数据存储等完整链路并通过data目录自动落盘结果source_code目录清晰分离业务与逆向模块降低二次开发门槛。 这个项目我盯了好一阵子才决定动手写。很多朋友问我淘宝评论到底怎么采集上来就搜爬虫教程结果发现复制粘贴的代码跑不了几天就失效核心原因就俩一个是没搞懂淘宝的接口签名逻辑另一个是不知道Cookie的维护坑在哪。这篇文章不教你怎么用现成轮子而是把整个逆向分析的思路、签名算法的定位过程、Python代码的落地实现以及我调试过程中踩过的坑一次性讲清楚。整个过程基于我自己实测通过的方案代码思路可以直接复用但请务必遵守平台规则控制采集频率仅用于个人学习研究。1. 项目整体设计与逆向思路拆解很多人不理解采集商品评论为什么要逆向。直白说淘宝的商品评论数据不是随便一个URL就能拿到的数据接口隐藏在H5页面里通过异步请求动态加载而且每次请求都带着一串加密参数。如果你直接用requests去请求评论接口返回的要么是空数据要么直接报错。想要稳定拿到评论数据就必须搞清楚这些参数是怎么生成的这就是逆向的起点。1.1 评论接口的调用链路先看数据流。手机端淘宝商品详情页往下滑动评论内容是分页异步加载的。对应的接口一般在h5api.m.taobao.com域名下路径通常是/h5/mtop.taobao.rate.detaillist/1.0/。这个接口通过POST方式提交关键的是请求体里的data字段和请求头里的x5sec、cookie。整个链路可以简单拆成四层商品详情页提供itemId商品ID这是评论查询的唯一入口参数。异步加载事件页面滚动或点击查看全部评价时触发数据请求。签名参数组装客户端JS会在请求发出前把参数按规则拼接、加密生成sign等校验参数。服务端校验淘宝网关校验参数是否合法不合法直接拒绝。这四个环节里最核心也最劝退的就是第三层——签名参数的生成逻辑。它不在服务端而在前端JS代码里所以我们需要抓取JS文件分析加密算法然后用Python重新实现一遍。1.2 为什么选择Python作为实现语言做这个项目有很多语言选择Node.js、Java都能写但我最终选了Python原因很现实生态齐全requests处理HTTP请求execjs调用JS环境BeautifulSoup或正则解析HTMLpandas做数据处理整个流程一套Python全搞定。验证快速逆向分析过程是反复试错的过程Python写脚本验证一个加密函数的输出几秒钟就能看到结果调试效率非常高。上手门槛低爬虫本身逻辑不复杂Python的语法特性让代码读起来像伪代码便于后期维护。当然Python也有短板性能上不如Java和Go。但对于采集评论这种量级的数据瓶颈在网络的IO等待上Python的并发模型完全够用。如果你未来要做大规模分布式采集可以基于这套原型改成Scrapy框架或Go版本。2. 核心技术难点签名机制与Cookie维护这一章节是整个项目的精华。我踩过的坑、花的时间90%都集中在这里。搞懂这一块淘宝评论采集就不再是玄学。2.1 token与sign淘宝请求的双重保险先讲一下淘宝接口校验的核心机制。所有mtop接口都依赖两个关键参数token和sign。token全称_m_h5_tk首次访问淘宝页面时由服务端下发存放在Cookie里形如_m_h5_tk8f7a9c3e4b5d...。它有一个特点通常半小时左右会过期一次。sign基于token和其他参数拼接后做MD5计算出来的摘要值。服务端会用同样的算法校验如果sign不对请求就会返回FAIL_SYS_TOKEN_EXOIRED或PARAM_INVALID之类的错误。这两个参数的关系可以用一句话概括token是密钥的基础sign是token和业务参数的摘要证明。sign的生成算法是整个逆向工程的第一目标。2.2 定位加密代码从抓包到Hook找到sign算法的过程并不神秘核心就是抓包 搜索关键词 下断点三步走。第一步用Chrome开发者工具或抓包工具Fiddler、Charles都可以监听请求。打开淘宝某商品详情页下滑触发评论加载在Network面板里找到上面的那个mtop接口。第二步查看该请求的Payload。你会发现有一个data字段里面是评论请求的具体参数还有一个sign参数是一串32位的十六进制字符串。接下来在Sources面板中全局搜索这串sign值或者搜索关键字sign和_m_h5_tk。第三步定位到生成sign的具体JS代码。通常你会发现类似这样的逻辑function generateSign(token, timestamp, appKey, data) { var str token timestamp appKey data; return md5(str); }不同版本可能会对data参数做排序或URL编码处理但整体结构大同小异。看到这段代码逆向的核心工作就完成了剩下的只是用Python翻译一遍。2.3 Cookie的时序问题最容易翻车的地方很多教程没讲透的是token的获取时机。这里有一个非常容易踩坑的细节第一次请求淘宝首页时服务端会下发一个_m_h5_tk但这个初始token是不完整版的通常为空或只有一个下划线开头的占位符。只有当你带着这个初始token去请求一次真实的业务接口比如商品详情接口后服务端才会在响应中返回完整的_m_h5_tk。换句话说token是二次下发的。直接用初始token去生成sign大概率会报错。正确做法是先用requests.Session登录淘宝页面拿到初始Cookie然后主动请求一次详情页获取更新后的Cookie最后再带着完整token去生成sign并请求评论接口。这三步的时序一步都不能错。3. 实操过程与核心环节实现终于到代码落地的环节了。这一节我会给出完整的实现思路和关键代码片段按模块拆解方便你直接对照搭建。3.1 项目结构规划先看一下整个项目的文件组织方式这是我实际使用的分层结构每个模块职责单一出问题好排查taobao_comment_spider/ ├── config/ │ └── settings.py # 配置文件存放appKey、接口URL等 ├── core/ │ ├── __init__.py │ ├── fetcher.py # 请求模块维护Cookie和Session │ ├── signer.py # 签名模块生成sign参数 │ ├── parser.py # 解析模块清洗评论数据 │ └── storage.py # 存储模块结果写入CSV/JSON ├── outputs/ # 数据输出目录 ├── requirements.txt └── main.py # 主入口这样的分层结构好处是每一块的逻辑都可以单独验证。比如signer.py写好后可以先跑一个单元测试传固定的参数验证输出和浏览器中的sign是否一致确认无误后再去联调其他模块。3.2 关键依赖与环境准备需要安装的Python包并不多良心推荐精简原则pip install requests pip install execjs pip install pycryptodome这里重点说一下execjs。有些人会建议你用subprocess调Node.js或用py_mini_racer但实际用下来execjs在易用性和兼容性上表现最稳定。它会自动调用你本机的Node.js环境来执行JS代码这样如果有些加解密算法用Python重写太麻烦可以直接把提取出来的JS片段扔进去跑省时省力。3.3 signer模块签名算法的Python实现假设通过逆向分析我们得到的sign算法是sign md5(token timestamp appKey dataBody)用Python实现起来非常简洁import hashlib import time import json def generate_sign(token: str, app_key: str, data: dict) - tuple: timestamp str(int(time.time() * 1000)) # 注意data要做JSON序列化且不能有空格 data_str json.dumps(data, separators(,, :), ensure_asciiFalse) raw_string f{token}{timestamp}{app_key}{data_str} sign hashlib.md5(raw_string.encode(utf-8)).hexdigest() return timestamp, sign这里的几个细节需要特别注意第一JSON序列化格式。json.dumps默认会在冒号后加空格而JS里通常是不加空格的所以必须用separators(,, :)来压缩。否则生成的sign永远对不上。第二timestamp的取值。必须和请求头里x-mini-wua、x-umt等参数保持同一个时间基准不要先算一个时间戳存着过了几秒再拿去生成sign时间偏差会导致校验失败。第三token的格式。传入的token是不带_m_h5_tk前缀的原始值。如果Cookie里的值是_m_h5_tk8f7a...取的时候要只截取后面的部分。3.4 fetcher模块Session与Cookie的维护这一块是稳定性的大头。核心逻辑分三步import requests class Fetcher: def __init__(self): self.session requests.Session() self.headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.10, Referer: https://h5.m.taobao.com/, } def init_cookie(self, item_id: str): # 第一步访问商品详情页拿到初始token detail_url fhttps://h5.m.taobao.com/awp/core/detail.htm?id{item_id} resp self.session.get(detail_url, headersself.headers, timeout10) # 第二步主动请求一次业务接口触发完整token下发 # 这里可以请求一个商品的基础信息接口 self._refresh_token() return self.session.cookies.get(_m_h5_tk, ) def _refresh_token(self): # 用当前cookie去请求一个可选接口强制更新token pass关键点是init_cookie里第一步和第二步之间的时间间隔不能太短也不能太长。我测试下来的经验是间隔1-2秒比较稳太快会被服务端的风控逻辑判定为异常访问太慢token又可能已经过期。3.5 评论数据的解析与清洗拿到接口返回的JSON后评论数据嵌套比较深通常在data.rateList字段下。每条评论的主要字段包括rateContent评论文本内容rateDate评论时间auctionSku购买的商品规格颜色、尺码displayUserNick脱敏后的用户名appendComment追加评论有则是一个独立对象解析时用jsonpath库最方便import jsonpath def parse_comments(data): rate_list jsonpath.jsonpath(data, $.data.rateList) or [] comments [] for rate in rate_list: item { content: rate.get(rateContent, ), date: rate.get(rateDate, ), sku: rate.get(auctionSku, ), user: rate.get(displayUserNick, ), } if rate.get(appendComment): item[append_content] rate[appendComment].get(appendContent, ) comments.append(item) return comments清洗环节不要嫌麻烦。实测下来评论数据里经常会掺杂表情符号、HTML标签如span classexpr以及韩文日文等非目标语言字符。建议统一用正则把[^]标签去掉再用emoji库或unicode范围过滤掉表情符号。否则后续做词频分析或情感分析时数据噪声会非常大。3.6 主流程串联与分页处理评论接口是分页的每一页通过pageSize和pageNumber参数控制。主流程需要循环请求直到返回的评论数量小于pageSize或者达到指定页数def fetch_all_comments(item_id, pages10): fetcher Fetcher() token fetcher.init_cookie(item_id) all_comments [] for page in range(1, pages 1): data { itemId: item_id, pageSize: 20, pageNumber: page, } timestamp, sign generate_sign(token, APP_KEY, data) resp_data fetcher.fetch_comments(data, timestamp, sign) comments parse_comments(resp_data) if not comments: break all_comments.extend(comments) print(f第{page}页获取{len(comments)}条评论) # 非常重要每页之间必须sleep time.sleep(random.uniform(3, 6)) return all_comments为什么每页之间必须sleep这是整个项目能不能长期跑下去的命脉。淘宝的风控非常灵敏如果你在短短几秒内连续请求几十次大概率会触发滑块验证或者被临时封禁。我建议的节奏是每页间隔至少3秒且间隔时间随机化不要用固定的sleep参数因为固定间隔在风控看来反而是明显的机器行为。4. 常见问题与排查技巧实录开发这个项目的过程中我踩了不少坑这里整理成清单方便你对照排查。问题现象可能原因解决方案请求返回FAIL_SYS_TOKEN_EXOIREDtoken已过期或timestamp偏差过大重新获取token检查时间戳是否用的同一个值请求返回PARAM_INVALIDsign生成时data格式不对多半是JSON序列化多了空格检查separators(,, :)参数返回数据只有第一页翻页后报错翻页时token失效或session被重置确认翻页用的是同一个sessiontoken定期刷新评论内容大量是***触发了评论的敏感词过滤重新登录更换IP降低采集频率请求偶发跳转到登录页Cookie失效或触发风控检查Cookie有效期增加延时考虑加代理池execjs运行JS报中文乱码编码问题Python侧用encodingutf-8读取JS文件4.1 案例sign循环对不上这是新手阶段最容易崩溃的场景。你复制了JS算法用Python重写了一遍可生成的sign和浏览器里看到的始终不一样。遇到这个问题我的排查思路是不要硬看代码用数据说话。把浏览器请求里的token、timestamp、appKey、data四个值原封不动提取出来手动喂给Python的generate_sign函数看输出的MD5是否和浏览器一致。如果一致说明算法本身没问题问题出在请求时动态参数上比如timestamp用的是毫秒还是秒data里是否有动态字段。如果不一致那就是算法细节有出入常见的是data里的字段顺序有些版本的淘宝会对data的key做ASCII排序再拼接序列化。4.2 案例Cookie的二次更新问题这个问题我反复提因为太容易忽略。很多朋友在请求评论接口时报token错误但检查一下Cookie里的_m_h5_tk发现存在啊为什么还会报错原因就是我在第2.3节里说的初始token和可用token不是一个东西。淘宝的设计逻辑是初始token的作用仅仅是换取完整token的身份凭证你必须带着它去主动请求一次业务接口服务端才会在响应Set-Cookie里下发完整token。如果你拿到初始token就直接拿去做sign相当于拿半成品当成品用肯定不行。解决方案也很直接在init_cookie方法里强制请求一次商品概要接口让服务端刷新Cookie然后再提取。4.3 关于字体反爬评论数据里有一个额外的坑价格或者评分数字有时会通过自定义字体反爬返回。也就是说接口返回的JSON里价格字段可能是718但在页面上显示的却是580这个映射关系藏在.ttf字体文件里。字体反爬的处理方案比较重一般需要下载字体文件用fontTools库解析cmap表建立字形到真实数字的映射。但就评论采集这个场景来说我实测下来评论数据本身很少做字体加密基本只有价格区间字段会出现。如果你不需要价格数据可以无视这个问题直接忽略相关字段即可。5. 合规使用与长期稳定运行的建议技术说完了最后聊一点实际的。写爬虫的人多少都收到过那条你的请求已被拦截的提示也见过IP被限甚至被封的情况。这个话题绕不开我做几点实在的建议。5.1 明确采集边界淘宝商品评论本质上是用户公开发布的UGC内容采集这类数据用于个人学习、数据分析目前没有明确的法律禁令。但有几个红线必须守住不得将采集的数据用于商业用途特别是倒卖用户个人信息。不得对目标平台造成访问压力建议单IP的并发数控制在极低水平日采集量也尽量克制。不得绕过平台的登录强校验机制去采集非公开数据比如会员专享评论内容。技术能力越大越要克制。写这个项目更重要的是理解爬虫与反爬的攻防思路而不是真的去大规模采集数据。5.2 如何让脚本更耐用如果你希望脚本在较长周期内持续稳定运行有几个工程化经验可以借鉴第一代理是必须的。不要等到IP被封了才想起代理。可以在配置里预留代理池接口让requests的session绑定代理。注意代理的切换频率也不要太高一个代理至少使用5-10分钟再切换。第二异常捕获要分层。网络超时、解析异常、Cookie失效三者的处理策略完全不同。超时就直接重试解析异常要记录日志并跳过Cookie失效则需要重新执行初始化流程。第三断点续爬。把已采集的评论ID写入一个本地文件或数据库每次启动时加载用于去重。这样哪怕跑到一半断网了重启脚本不会重复采集也不用从头来。第四随机化所有参数。User-Agent、请求间隔、翻页顺序尽量做随机化处理。一个固定UA跑几千个请求是风控最喜欢抓的特征。5.3 这套方案的扩展思路这个项目的底层逻辑逆向分析接口签名并用Python模拟实现不只适用于淘宝评论。同一套方法论可以直接迁移到天猫商品评论采集接口结构几乎一样只换appKey和接口名淘宝商品搜索接口不同接口的sign算法一致只拼不同data闲鱼、京东等平台的部分H5接口算法细节不同但分析路径相同可以说这个项目最大的收获不是那个爬虫程序本身而是掌握了一套从浏览器到代码的闭环分析方式。下次再碰到一个陌生的数据接口你就知道该从哪里下手了。6. 写在最后的调试心得整个项目从立项到跑通我花了大几个晚上。回头想想真正卡住我的不是代码而是对Cookie生命周期和sign拼接细节的理解。一旦把这些隐含规则打通剩下的代码实现反而水到渠成。这里分享一个提升排查效率的小技巧在所有调试阶段先在浏览器里用console手动执行一遍JS签名函数记录下输出再和Python的输出做比对。别急着联调整个流程先确保最小的签字函数是一致的再逐步放大到完整请求链路。这个习惯帮我省了至少一个晚上的调试时间。另外代码里的日志输出要舍得写。我在初版脚本里几乎每个关键节点都打了日志什么已获取初始token、sign生成成功、第3页返回200条数据之类的。后期跑批量采集的时候这些日志成了定位问题最重要的线索。尤其是遇到被封IP或token失效时通过日志判断是哪一步出了问题远比拍脑袋猜有用得多。如果你照着这篇文章的思路把代码搭出来哪怕最终的实现细节和我的版本有出入只要跑通了这套方法论就真正是你的了。本文还有配套的精品资源点击获取
返回列表