京东详情主接口后台实测只有 78%,描述、图文、销量这些字段还经常拿不全。这篇把 item_get_desc(98%)和 item_review(99%)两个侧链路接口的请求和报文逐字段拆开,给一条能落地的补字段链路。
一、先看清成功率分布,再决定谁当主力
做京东数据链路,第一步是把后台实测的成功率数据摆出来,直接决定每个接口的角色:
| 接口 | 实测成功率 | 角色定位 |
|---|---|---|
item_get | 78% | 主详情,允许失败,但别押宝 |
item_get_pro | 78% | 富详情,与主接口同水位 |
item_get_desc | 98% | 描述与图文,侧链路主力 |
item_review | 99% | 评价数据,销量信号替代 |
item_history_price | 75% | 历史价格,低频使用 |
item_search | 28% | 避开主链路,只做人工触发补采 |
这个分布里有两条结论。第一,京东主链路天然有约五分之一的调用落空,落空之后怎么补字段比怎么重试更重要——盲目对 78% 的接口做三次重试,期望成功率也只有 98.9%,代价却是三倍调用,而且碰上缓存了脏结果的情况重试也救不回来。第二,描述类字段有 98% 的专用接口,根本不该从主接口硬拿——侧链路不是备胎,是该当主力的地方。
调用习惯上再给两个小建议:类目、详情这类读多写少的数据,cache保持默认的yes,实测能明显压低execution_time;只有调试参数写没写对的时候才临时切no,确认完记得切回去,不然每次都是源站实时取。
还有一个容易忽略的口径:京东item_get那 22% 的落空并不都长一个样。有的是直接吐 4 开头的错误码,有的是error_code为0000但item里缺关键字段——前者不计费、可以放心重试,后者已经计费了,重试纯属重复花钱。所以编排逻辑要按"错误码"和"字段完整度"两个维度分别兜底,这也是下面几节的主线。
二、item_get_desc:请求与报文拆解
网关不变,https://api-gw.onebound.cn/jd/item_get_desc/。公共参数还是key / secret / api_name / cache / result_type / lang那一套,业务参数只有num_iid(京东商品 ID,纯数字或带J_前缀均可)。这里有个和 1688 不一样的细节:京东的num_iid是 10 位左右的纯数字,不像 1688 报价 ID 那么长,但入参清洗的规则不变——去掉J_前缀再传,同一款商品就不会因为入口不同被存成两条记录:
curl"https://api-gw.onebound.cn/jd/item_get_desc/?key=你的key&secret=你的secret&num_iid=100012043978&cache=yes&result_type=json&lang=cn"返回报文的item部分核心字段:
{"error_code":"0000","item":{"num_iid":"100012043978","description":"<p><img src=\"//img13.360buyimg.com/n1/jfs/t1/aaa.jpg\"/></p><p>面料:90%棉...</p>","desc_imgs":["http://img13.360buyimg.com/n1/jfs/t1/aaa.jpg"]}}字段口径逐个说:
| 字段 | 含义 | 实测注意点 |
|---|---|---|
description | 详情 HTML 源码 | 协议相对 URL,//开头,落库前要补https: |
desc_imgs | 已提取的纯图数组 | 做图文展示直接用它,别自己再解析 HTML |
num_iid | 回传的商品 ID | 校验一下与请求一致,防止缓存串号 |
三个实操坑都在description上:一是图片协议相对地址,前端直接<img src>会因协议问题挂掉;二是京东详情 HTML 里混有运营端遗留的样式脚本垃圾,清洗时要按白名单过滤标签而不是黑名单;三是详情图文里经常藏着规格表(面料成分、尺码),这是主接口拿不到、选品又最需要的字段。规格表在报文里通常长成<table><tr><td>面料</td><td>90%棉</td></tr>...</table>,抽取时按行拆tr、按列拆td,两列的当键值对存,三列以上的整行存进 JSON——别试图把所有规格表解析成统一结构,京东商家做表的手法五花八门,解析得越"聪明"碎得越厉害。动手前建议先拿真实报文对照一遍再写解析,调试入口在 开放平台控制台,需要的自取。
三、HTML 清洗:把描述变成可存储的干净字段
importrefrombs4importBeautifulSoup ALLOWED={"p","img","table","tr","td","br","strong"}defclean_desc(html:str):"""白名单过滤 + 协议修复 + 纯文本抽取"""ifnothtml:return"",[]soup=BeautifulSoup(html,"html.parser")fortaginsoup.find_all():iftag.namenotinALLOWED:tag.unwrap()forimginsoup.find_all("img"):src=img.get("src","")ifsrc.startswith("//"):img["src"]="https:"+src imgs=[i["src"]foriinsoup.find_all("img")]text=re.sub(r"\s+"," ",soup.get_text(" ",strip=True))returntext,imgs清洗结果建议存两份:desc_text(纯文本,做搜索和成分抽取用)和desc_imgs(图数组,做前端展示用),原始 HTML 再留一份在raw_json里。归一化一定会漏东西,能回捞原始报文比重新调接口便宜得多——毕竟0000是计费的。
四、item_review 99%:销量信号的替代口径
京东item_get的销量字段缺失是常态,而评价接口实测99%,是最稳的替代信号源:
defjd_reviews(num_iid,key,secret,page=1):r=call("jd","item_review",num_iid=num_iid,page=page,key=key,secret=secret)ifr.get("error_code")!="0000":returnNonereturn{"total":r.get("comments_count"),# 评价总数"good_rate":r.get("good_rate"),# 好评率"tags":r.get("tags",[]),# 高频评价标签"latest":[c["content"]forcinr.get("comments",[])[:10]],}口径要摆正:评价总数只能做相对排序,不能当绝对销量。两个商品比"哪个更热"用它没问题,换算成实际出货量就不行。另外tags(如"质量好"“物流快”)是做差评分析和选品文案的好素材,主接口完全没有这个信息;comments里翻车的具体描述攒够样本后,反过来就是 1688 选品时的避坑清单。
五、降级编排:谁失败谁补位
deffetch_jd_item(num_iid,key,secret):main=call("jd","item_get",num_iid=num_iid,key=key,secret=secret)item=main["item"]ifmain.get("error_code")=="0000"else{}ifnotitem.get("desc_text"):desc=call("jd","item_get_desc",num_iid=num_iid,key=key,secret=secret)ifdesc.get("error_code")=="0000":text,imgs=clean_desc(desc["item"]["description"])item["desc_text"],item["desc_imgs"]=text,imgs item["reviews"]=jd_reviews(num_iid,key,secret)# 异步更好returnitemorNone关于重试成本多说一句:失败如果落在4000 / 4001 / 4002 / 4017这几个请求侧错误码上,按官方口径不计费,可以放心重试;但拿到0000却缺字段的调用已经计费了,这种情况下重试主接口不如直接切侧链路——78% 的主接口重试期望收益,跑不过 98% 的侧接口。
六、收尾
这套"主接口 + 侧链路补位"的编排跑下来,京东链路的字段完整度能从主接口单打独斗的水平提上来,而平均调用成本反而更低。最后一条建议:给每个字段记一个source_api,后续哪个接口水位变化(比如主接口哪天跌到 70%)能立刻知道该从哪里补。