摘要:本文记录京东JD.item_search 商品搜索接口完整开发过程,覆盖开放平台签名算法、分页循环拉取、超时 / 签名错误 / 限流等异常处理方案,附可直接调试的 Python代码,适合竞品采集、电商市场调研场景,告别爬虫。
一、接口前置说明
接口标识:JD.item_search (Taobaoapi2014前往体验)
网关地址: c0b.cc/R4rbK2
用途:关键词检索京东商品列表,支持类目、价格、排序筛选
二、签名规则拆解(京东开放平台通用)
剔除参数中值为空的 key
参数按照 key 字典序升序排序
按key+value直接拼接成字符串,末尾拼接 app_secret
MD5 加密,结果转为大写,作为 sign
很多人调不通,基本都是排序错、时间时区不对、空格多了。
三、完整 Python 实现(签名 + 分页 + 异常捕获)
import hashlibimport requestsimport time
from typing import Optional, Dict, Listclass JdUnionSearchApi:
def __init__(self, app_key: str, app_secret: str):
self.app_key = app_key
self.app_secret = app_secret
self.gateway = "https://api.jd.com/routerjson"
@staticmethod
def get_sign(params: Dict[str, str], secret: str) -> str:
# 按key升序排序
sorted_pairs = sorted(params.items(), key=lambda x: x[0])
raw_str = "".join([f"{k}{v}" for k, v in sorted_pairs]) + secret
md5_val = hashlib.md5(raw_str.encode("utf-8")).hexdigest()
return md5_val.upper()
def search_goods(self,
keyword: str,
page_index: int = 1,
page_size: int = 20,
sort: Optional[str] = None,
cid: Optional[str] = None,
price_start: Optional[int] = None,
price_end: Optional[int] = None ) -> dict:
"""单次搜索一页商品"""
params = {
"method": "jd.union.open.goods.search",
"app_key": self.app_key,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
"keyword": keyword,
"pageIndex": str(page_index),
"pageSize": str(page_size)
}
# 可选参数,不为空才加入 if sort:
params["sort"] = sort if cid:
params["cid"] = cid if price_start is not None:
params["priceStart"] = str(price_start)
if price_end is not None:
params["priceEnd"] = str(price_end)
params["sign"] = self.get_sign(params, self.app_secret)
try:
resp = requests.get(self.gateway, params=params, timeout=10)
resp.raise_for_status()
return resp.json()
except requests.exceptions.Timeout:
return {"code": -1, "msg": "请求超时"}
except requests.exceptions.HTTPError as e:
return {"code": -2, "msg": f"HTTP异常:{str(e)}"}
except Exception as e:
return {"code": -99, "msg": f"未知异常:{str(e)}"}
def fetch_all_pages(self, keyword: str, max_page: int = 5, sleep_sec: float = 1.2, **kwargs) -> List[dict]:
"""循环分页拉取,内置限流休眠,防止被封"""
all_goods = []
for page in range(1, max_page + 1):
res = self.search_goods(keyword=keyword, page_index=page, **kwargs)
if res.get("code", 0) != 0:
print(f"第{page}页请求失败:{res.get('msg')}")
break
# 根据京东返回结构提取商品数组,这里根据实际返回调整key
data = res.get("jd_union_open_goods_search_response", {})
result = data.get("result", {})
goods_list = result.get("goodsList", [])
if not goods_list:
print(f"第{page}页无商品,停止翻页")
break
all_goods.extend(goods_list)
print(f"成功获取第{page}页,本页{len(goods_list)}条")
time.sleep(sleep_sec)
return all_goodsif __name__ == "__main__":
# 替换为自己的密钥
ak = "YOUR_APP_KEY"
sk = "YOUR_APP_SECRET"
api = JdUnionSearchApi(ak, sk)
goods = api.fetch_all_pages(keyword="无线充电器", max_page=3, page_size=20)
print(f"一共拿到商品:{len(goods)} 条")四、异常分类处理思路(生产重点)
签名错误(sign invalid)
排查:时间戳时区、参数是否有多余空格、是否漏删空值参数、排序是否字典序
接口返回无数据
关键词是否合规、类目 ID 是否正确、价格区间是否没有商品;部分排序模式下返回量会变少
频繁返回拒绝访问 / 403
触发平台限流,加大sleep_sec,分布式场景建议做令牌桶限流
偶尔超时
增加重试装饰器,设置最大重试次数,不要无限重试
字段不稳定、部分字段偶尔缺失
取字段时全部用 .get(),不要直接下标取值,防止 KeyError 崩溃
五、分页踩坑实录
京东这个接口不支持无限深度翻页,pageIndex 拉到后面会返回空,属于平台限制,不是 bug
总数量是估算值,不能用来精确判断还有多少页
pageSize 有上限,不要盲目设置很大,容易被拦截
六、落地业务延伸
拿到商品列表 skuId 后,可以串联:
京东商品详情API:获取原价、券后价、库存
评论 API:抓取评价做 NLP 情感分析
数据入库,定时任务做价格监控、竞品榜单、选品看板
七、开发小结
京东联盟搜索接口相比爬虫稳定很多,但签名、时区、限流是三大高频卡点。工程化时建议单独封装签名工具、统一异常码、增加日志、做请求间隔控制。整套链路可以直接用于跨境选品、竞品价格监控、电商市场数据采集。