在电商开发圈摸爬滚打这些年,京东商品评论API的“反人类”设计,至今想起来都让我头皮发麻。本以为只是拉取用户评论这么简单,结果从签名验证到数据解析,再到限流管控,每一步都藏着能让你熬夜调试的坑。今天就把这些年踩过的雷、攒的可落地代码全抖出来,给做商家工具、评论分析的朋友避避雷。
一、初次对接:漏传一个参数,签名调试到凌晨三点
第一次接京东评论接口时,我自信满满地照搬了商品接口的签名逻辑——按参数ASCII排序、MD5加密,结果连续返回10001签名错误。翻遍京东开放平台文档,没找到任何异常提示;对比官方示例,参数也没少传。直到在开发者论坛的一个沉帖里看到:京东评论接口必须传“client_type”参数(值为“pc”或“app”),否则签名必错,且错误信息不提示“参数缺失”。
我当时对着加密后的字符串比对了4小时,甚至怀疑是编码问题(比如中文转义),最后加上client_type参数,接口瞬间通了。自此,我写京东评论接口的签名函数时,都会把这个“隐藏参数”标红:
import hashlib
import time
import urllib.parse
def generate_jd_comment_sign(params, app_secret):
"""生成京东商品评论接口签名(必传client_type!)"""
# 1. 强制添加client_type,评论接口特有要求,漏传必错
params["client_type"] = "pc"
# 2. 过滤空值,按参数名ASCII排序(京东对顺序要求严格,差一个字符都不行)
sorted_params = sorted([(k, v) for k, v in params.items() if v is not None], key=lambda x: x[0])
# 3. 参数值URL编码(保留~!@#$&*等特殊字符,避免转义出错)
encoded_params = [(k, urllib.parse.quote(str(v), safe='~!@#$&*()_+=')) for k, v in sorted_params]
# 4. 拼接成key=value&key=value格式
query_str = "&".join([f"{k}={v}" for k, v in encoded_params])
# 5. 首尾加app_secret,MD5加密后转大写(京东评论接口用MD5,不是SHA1)
sign_str = f"{app_secret}{query_str}{app_secret}"
return hashlib.md5(sign_str.encode()).hexdigest().upper()
# 使用示例
params = {
"method": "jd.union.open.comment.get",
"app_key": "your_app_key",
"sku_id": "100028345678", # 商品SKU(京东评论接口用SKU,不是商品ID)
"page": 1,
"page_size": 20,
"timestamp": str(int(time.time())) # 秒级时间戳,不是毫秒!
}
params["sign"] = generate_jd_comment_sign(params, "your_app_secret")
二、数据解析:主评追评分开藏,客户投诉“评论少一半”
系统上线后没几天,运营就反馈:“用户说咱们显示的评论数比京东详情页少一半!” 我赶紧查日志,发现接口返回的评论分了两个字段——comments是主评,after_comments是追评,而我只解析了comments,直接漏了追评数据。
更坑的是,追评里还藏着“追加图片”,after_comments的images字段和主评结构一致,但部分老评论没有这个字段,直接取值会报KeyError。我连夜重构了解析函数,专门整合主评、追评和图片:
import requests
def parse_jd_comments(response_data):
"""解析京东评论数据,整合主评、追评和图片"""
all_comments = []
# 1. 处理主评(必存在,字段相对稳定)
main_comments = response_data.get("result", {
}).get("comments", [])
for main in main_comments:
# 提取主评图片(无图片时返回空列表,避免KeyError)
main_images = [img.get("url") for img in main.get("images", []) if img.get("url")]
main_info = {
"comment_id": main.get("id"),
"user_nick": main.get("nickname", "匿名用户"), # 京东昵称会脱敏,如“李**”
"score": int(main.get("score", 0)), # 1-5分,对应星级
"content": main.get("content", ""),
"create_time": main.get("create_time"), # 格式:yyyy-MM-dd HH:mm:ss
"images": main_images,
"comment_type": "main" # 标记主评
}
all_comments.append(main_info)
# 2. 处理追评(部分评论无追评,需判断是否存在)
after_comments = response_data.get("result", {
}).get("after_comments", [])
for after in after_comments:
after_images = [img.get("url") for img in after.get("images", []) if img.get("url")]
after_info = {
"comment_id": after.get("id"),
"user_nick": after.get("nickname", "匿名用户"),
"score": int(after.get("score", 0)), # 追评也有评分,部分场景会和主评不同
"content": after.get("content", ""),
"create_time": after.get("create_time"),
"images": after_images,
"comment_type": "after" # 标记追评
}
all_comments.append(after_info)
return all_comments
# 调用示例
response = requests.post("https://api.jd.com/routerjson", data=params)
parsed_comments = parse_jd_comments(response.json())
print(f"共解析到{len(parsed_comments)}条评论(含{len([c for c in parsed_comments if c['comment_type']=='after'])}条追评)")
三、限流暴击:批量采集10分钟,接口被封24小时
最让我崩溃的一次,是帮客户做“竞品评论分析”时触发了京东限流。当时要采集10个SKU的评论,每个SKU爬5页,我没控制频率,10分钟内发了60次请求——结果直接收到京东开放平台的邮件:“接口调用频率超过限制,封禁24小时”。
后来查文档才知道,京东评论接口对免费开发者的限流是“10次/分钟”,比商品接口严一倍(商品接口20次/分钟),且超过后不是临时限制,是直接封号。痛定思痛后,我用“滑动窗口算法”写了个限流类,严格控制调用节奏:
import time
from collections import deque
class JDCommentLimiter:
def __init__(self, max_calls=10, window_seconds=60):
"""京东评论接口限流:max_calls次/窗口秒数"""
self.max_calls = max_calls # 免费用户默认10次/分钟
self.window = window_seconds
self.call_records = deque() # 存储每次调用的时间戳
def can_call(self):
"""判断是否可发起请求,可调用则记录时间戳"""
now = time.time()
# 移除窗口外的调用记录(比如1分钟前的记录)
while self.call_records and now - self.call_records[0] > self.window:
self.call_records.popleft()
# 未达上限则记录当前调用时间
if len(self.call_records) < self.max_calls:
self.call_records.append(now)
return True
return False
def wait_for_call(self):
"""等待到可调用状态,返回等待时间"""
while not self.can_call():
# 计算需等待的时间(窗口剩余时间)
wait_time = self.window - (time.time() - self.call_records[0])
time.sleep(wait_time + 0.1) # 多等0.1秒,避免边界问题
return True
# 使用示例:批量采集多个SKU的评论
limiter = JDCommentLimiter(max_calls=10, window_seconds=60)
sku_list = ["100028345678", "100032145678", "100045678901"] # 待采集SKU
for sku in sku_list:
# 等待到可调用状态
limiter.wait_for_call()
# 发起评论请求(此处省略参数构建和请求逻辑)
print(f"正在采集SKU {sku} 的评论...")
# 模拟接口调用耗时
time.sleep(1)
四、京东评论API的4个“隐形坑”(血的教训)
做了3年京东评论系统,我总结出这些接口“隐形坑”,踩中任何一个都可能让你熬夜改BUG:
- SKU≠商品ID,传错直接返回空数据:京东评论接口只认
sku_id(商品规格ID),传product_id(商品主ID)会返回“无评论数据”,但错误信息不提示“参数错误”,新手很容易踩。 - 追评不是“附属品”,单独存储在after_comments:文档里没明确说追评和主评分开,默认只解析comments会漏50%数据,运营反馈后才发现。
- 时间戳是“秒级”,别和商品接口混了:京东商品接口支持毫秒级时间戳,但评论接口只认10位秒级时间戳(如1699999999),传13位会报签名错误。
- 免费接口别碰“高并发”:10次/分钟的限流卡得很死,商业场景一定要提前申请“企业级额度”,否则批量采集就是空谈——我曾为了绕开限流,不得不在不同服务器部署,结果被京东判定“恶意调用”,连企业额度都降了级。
最后:给新手的3句真心话
- 别信“文档写全了”:京东评论接口的
client_type参数,文档里只在“常见问题”里提了一句,不仔细看根本发现不了,建议调用前先查“错误码对照表”(10001签名错误优先查是否漏传参数)。 - 评论图片要做“去重”:部分用户会重复上传同一张图,接口会返回多个相同URL,前端展示前最好去重,否则会浪费带宽。
- 保存原始响应日志:京东偶尔会微调接口字段(比如曾把
score改成star,3天后又改回来),保存原始日志能快速定位“字段突变”问题,避免排查时抓瞎。