在电商平台的技术生态中,关键字搜索是连接用户需求与商品资源的核心入口。京东作为国内领先的电商平台,其商品搜索接口具有数据量大、筛选维度丰富、实时性要求高等特点。本文将全面解析京东关键字搜索商品列表接口的技术实现,从核心参数设计、签名机制到分布式调用策略,结合完整代码示例,帮助开发者构建高效、稳定的商品搜索系统,满足不同业务场景的需求。
一、接口技术架构与核心参数解析
京东关键字搜索接口基于分布式服务架构,采用 "网关 + 微服务" 模式处理海量搜索请求。其核心原理是通过分词系统对用户输入的关键字进行处理,结合筛选条件在商品库中进行精确匹配,最终返回按相关性排序的商品列表。
核心参数体系
京东搜索接口的参数设计兼顾了灵活性和精确性,主要分为以下几类:
| 参数类别 | 具体参数 | 作用说明 |
|---|---|---|
| 基础参数 | keyword | 搜索关键字,必填参数,支持中文、英文及混合输入 |
page | 页码,默认 1,最大支持 50 页 | |
pageSize | 每页条数,支持 10/20/30/50,默认 20 | |
| 筛选参数 | priceFrom/priceTo | 价格区间筛选,单位为元 |
brandId | 品牌 ID 筛选,可通过品牌列表接口获取 | |
cateId | 类目 ID 筛选,用于限定商品所属类目 | |
sortType | 排序方式:0 - 综合,1 - 价格升序,2 - 价格降序,3 - 销量降序 | |
| 扩展参数 | isSelf | 是否自营:0 - 全部,1 - 仅自营 |
hasStock | 是否有货:0 - 全部,1 - 仅有货 | |
| 认证参数 | app_key/sign/timestamp | 接口调用安全验证参数 |
响应数据结构
接口返回的商品列表数据采用多层 JSON 结构,包含搜索结果统计、商品基本信息、价格库存等核心内容:
json
{
"code": 200,
"message": "success",
"result": {
"totalCount": 1256, // 总商品数
"page": 1,
"pageSize": 20,
"goodsList": [
{
"skuId": "100012345678", // 商品ID
"name": "XX品牌无线蓝牙耳机", // 商品名称
"brandName": "XX品牌", // 品牌名称
"price": "299.00", // 价格
"marketPrice": "399.00", // 市场价
"imageUrl": "https://img10.360buyimg.com/n1/s450x450/xxx.jpg", // 商品主图
"commentCount": 1254, // 评论数
"goodRate": 96, // 好评率(%)
"isSelf": 1, // 是否自营
"stockState": 33 // 库存状态(33表示有货)
}
// 更多商品...
]
}}点击获取key和secret
二、开发环境准备与依赖配置
环境要求
开发语言:Python 3.8+(推荐 3.10 版本,支持类型注解)
依赖库:
requests(HTTP 请求)、pandas(数据处理)、redis(缓存)、tenacity(重试机制)运行环境:支持 Windows 10/11、macOS 12+、Linux CentOS 7+
依赖安装
通过 pip 命令快速安装所需依赖:
bash
pip install requests pandas redis tenacity
开放平台配置
登录京东开放平台完成开发者注册
创建应用并获取
app_key和app_secret申请 "商品搜索" 接口权限(接口名称:
jingdong.search.goods)确认接口调用限制:默认 200 次 / 分钟,单页最大 50 条数据
三、接口开发实战实现
步骤 1:签名生成工具实现
京东接口采用 HMAC-SHA256 算法生成签名,确保请求合法性:
python
运行
import timeimport hashlibimport hmacimport urllib.parsedef generate_jd_sign(params, app_secret):
"""
生成京东接口签名
:param params: 请求参数字典
:param app_secret: 应用密钥
:return: 签名字符串
"""
# 1. 按参数名ASCII升序排序
sorted_params = sorted(params.items(), key=lambda x: x[0])
# 2. 拼接为key=value&key=value格式
query_string = urllib.parse.urlencode(sorted_params)
# 3. HMAC-SHA256加密并转为大写
signature = hmac.new(
app_secret.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256 ).hexdigest().upper()
return signature步骤 2:搜索接口客户端封装
封装完整的搜索接口调用逻辑:
python
运行
import requestsimport jsonfrom typing import Dict, Optional, Anyclass JdSearchAPI:
def __init__(self, app_key: str, app_secret: str):
self.app_key = app_key
self.app_secret = app_secret
self.api_url = "https://api.jd.com/routerjson"
def search_goods(self,
keyword: str,
page: int = 1,
page_size: int = 20,** kwargs) -> Optional[Dict[str, Any]]:
"""
搜索商品列表
:param keyword: 搜索关键字
:param page: 页码
:param page_size: 每页条数
:param kwargs: 其他筛选参数
:return: 搜索结果字典
"""
# 1. 构建基础参数
params = {
"method": "jingdong.search.goods",
"app_key": self.app_key,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"format": "json",
"v": "2.0",
"keyword": keyword,
"page": page,
"pageSize": page_size,
**kwargs }
# 2. 生成签名
params["sign"] = generate_jd_sign(params, self.app_secret)
try:
# 3. 发送POST请求
response = requests.post(
self.api_url,
data=params,
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=10
)
# 4. 处理响应
response.raise_for_status()
result = json.loads(response.text)
# 5. 错误处理
if "error_response" in result:
error = result["error_response"]
print(f"搜索失败: {error.get('msg')} (错误码: {error.get('code')})")
return None
return result.get("jingdong_search_goods_response", {}).get("result", {})
except Exception as e:
print(f"接口调用异常: {str(e)}")
return None步骤 3:搜索结果解析与处理
将原始搜索结果解析为结构化数据:
python
运行
import pandas as pddef parse_search_result(raw_data: Dict) -> tuple[Optional[pd.DataFrame], Optional[Dict]]:
"""
解析搜索结果
:param raw_data: 接口返回的原始数据
:return: 商品列表DataFrame和统计信息
"""
if not raw_data or "goodsList" not in raw_data:
return None, None
# 解析商品列表
goods_list = []
for goods in raw_data["goodsList"]:
goods_info = {
"sku_id": goods.get("skuId"),
"name": goods.get("name"),
"brand": goods.get("brandName"),
"price": goods.get("price"),
"market_price": goods.get("marketPrice"),
"image_url": goods.get("imageUrl"),
"comment_count": goods.get("commentCount", 0),
"good_rate": goods.get("goodRate", 0),
"is_self": goods.get("isSelf", 0),
"stock_state": goods.get("stockState", 0),
"is_in_stock": goods.get("stockState") == 33
}
goods_list.append(goods_info)
# 转换为DataFrame
df = pd.DataFrame(goods_list)
# 解析统计信息
stats = {
"total_count": raw_data.get("totalCount", 0),
"page": raw_data.get("page", 1),
"page_size": raw_data.get("pageSize", 20),
"total_page": (raw_data.get("totalCount", 0) + raw_data.get("pageSize", 20) - 1)
// raw_data.get("pageSize", 20)
}
return df, stats步骤 4:高级搜索功能实现
实现带复杂筛选条件的高级搜索:
python
运行
def advanced_search(api_client: JdSearchAPI,
keyword: str,
price_range: tuple = None,
sort_type: int = 0,
is_self: int = 0,
has_stock: int = 1,
pages: int = 1) -> Optional[pd.DataFrame]:
"""
高级搜索实现
:param api_client: 搜索API客户端实例
:param keyword: 搜索关键字
:param price_range: 价格范围元组(priceFrom, priceTo)
:param sort_type: 排序方式
:param is_self: 是否自营
:param has_stock: 是否有货
:param pages: 需要获取的页数
:return: 合并后的商品DataFrame
"""
all_goods = []
# 构建筛选参数
filters = {
"sortType": sort_type,
"isSelf": is_self,
"hasStock": has_stock }
# 添加价格筛选
if price_range and len(price_range) == 2:
filters["priceFrom"] = price_range[0]
filters["priceTo"] = price_range[1]
# 多页获取
for page in range(1, pages + 1):
print(f"获取第 {page} 页搜索结果...")
raw_result = api_client.search_goods(
keyword=keyword,
page=page,
page_size=50, # 每页最多50条
**filters )
if not raw_result:
continue
goods_df, stats = parse_search_result(raw_result)
if goods_df is not None and not goods_df.empty:
all_goods.append(goods_df)
print(f"第 {page} 页解析完成,共 {len(goods_df)} 条商品")
# 控制请求频率
time.sleep(1)
# 合并所有页面数据
if all_goods:
return pd.concat(all_goods, ignore_index=True)
return None步骤 5:完整调用示例
python
运行
if __name__ == "__main__":
# 配置应用信息
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
# 初始化API客户端
search_api = JdSearchAPI(APP_KEY, APP_SECRET)
# 执行高级搜索
result_df = advanced_search(
api_client=search_api,
keyword="无线蓝牙耳机",
price_range=(100, 500), # 价格100-500元
sort_type=3, # 按销量降序
is_self=1, # 仅自营商品
has_stock=1, # 仅显示有货
pages=3 # 获取3页数据
)
# 保存结果
if result_df is not None and not result_df.empty:
output_file = "jd_search_results.csv"
result_df.to_csv(output_file, index=False, encoding="utf-8-sig")
print(f"搜索完成,共获取 {len(result_df)} 条商品数据,已保存至 {output_file}")
# 打印部分结果
print("\n前5条商品信息:")
print(result_df[["name", "brand", "price", "comment_count", "good_rate"]].head())四、接口优化与高可用设计
搜索性能优化
1.** 分布式调用设计 **:
python
运行
from concurrent.futures import ThreadPoolExecutor, as_completeddef distributed_search(api_client, keyword, total_pages, max_workers=5):
"""分布式多线程搜索"""
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交所有页面请求
futures = {
executor.submit(
api_client.search_goods,
keyword=keyword,
page=page,
page_size=50
): page for page in range(1, total_pages + 1)
}
# 处理结果
for future in as_completed(futures):
page = futures[future]
try:
raw_data = future.result()
if raw_data:
df, _ = parse_search_result(raw_data)
if df is not None:
results.append(df)
print(f"分布式获取第 {page} 页完成")
except Exception as e:
print(f"分布式获取第 {page} 页失败: {str(e)}")
return pd.concat(results, ignore_index=True) if results else None2.** 搜索结果缓存 **:
python
运行
import redisimport picklefrom datetime import timedeltaclass CachedSearchAPI(JdSearchAPI):
def __init__(self, app_key, app_secret, redis_host="localhost", redis_port=6379):
super().__init__(app_key, app_secret)
self.redis = redis.Redis(host=redis_host, port=redis_port, db=0)
def get_cached_search(self, keyword, expire=300,** kwargs):
"""带缓存的搜索实现"""
# 生成缓存键(包含关键字和主要筛选条件)
cache_key = f"jd_search:{keyword}:{kwargs.get('priceFrom','')}-{kwargs.get('priceTo','')}:{kwargs.get('sortType',0)}"
# 尝试从缓存获取
cached_data = self.redis.get(cache_key)
if cached_data:
return pickle.loads(cached_data)
# 缓存未命中,调用接口
result = self.search_goods(keyword, **kwargs)
# 存入缓存(设置5分钟过期)
if result:
self.redis.setex(cache_key, timedelta(seconds=expire), pickle.dumps(result))
return result异常处理与限流
1.** 带重试机制的调用 **:
python
运行
from tenacity import retry, stop_after_attempt, wait_exponentialclass ReliableSearchAPI(JdSearchAPI): @retry( stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10) ) def search_with_retry(self,** kwargs): """带重试机制的搜索方法""" return super().search_goods(**kwargs)
2.** 流量控制实现 **:
python
运行
import timefrom collections import dequeclass RateLimitedSearchAPI(JdSearchAPI): def __init__(self, app_key, app_secret, max_calls=200, period=60): super().__init__(app_key, app_secret) self.max_calls = max_calls self.period = period self.calls = deque() def acquire(self): """获取调用许可""" now = time.time() # 移除过期的调用记录 while self.calls and now - self.calls[0] > self.period: self.calls.popleft() # 如果达到最大调用次数,等待 if len(self.calls) >= self.max_calls: sleep_time = self.period - (now - self.calls[0]) time.sleep(sleep_time + 0.1) self.acquire() self.calls.append(now) def search_with_rate_limit(self, **kwargs): """限流搜索""" self.acquire() return super().search_goods(** kwargs)
五、合规性与最佳实践
接口调用规范
严格遵守京东开放平台的调用频率限制,避免高频请求
关键字搜索应符合平台内容规范,不搜索违法违规内容
对获取的商品数据,应注明数据来源,不篡改原始信息
合理设置缓存时间,减轻平台服务器压力
常见错误处理
| 错误码 | 错误原因 | 解决方案 |
|---|---|---|
| 1001 | 签名错误 | 检查签名生成逻辑,确认参数排序正确 |
| 1003 | 接口权限不足 | 确认已申请搜索接口权限并通过审核 |
| 400 | 参数错误 | 检查 keyword 是否为空,pageSize 是否在允许范围 |
| 429 | 频率超限 | 优化请求频率控制,实现流量削峰 |
| 500 | 服务器错误 | 实现重试机制,记录详细错误日志 |
搜索体验优化建议
实现关键字自动补全功能,提升用户搜索效率
对搜索结果进行二次过滤和排序,满足业务需求
缓存热门搜索词结果,减少重复请求
实现搜索结果分页加载,提升前端渲染性能
六、总结与扩展应用
本文详细讲解了京东关键字搜索商品列表接口的开发全过程,从核心参数解析、签名生成到高可用调用实现,提供了完整的代码方案。通过合理使用缓存、分布式调用和流量控制等技术,可以显著提升搜索接口的性能和稳定性。
该接口的扩展应用场景包括:
电商比价系统:通过关键字搜索获取多平台商品价格进行对比
竞品分析工具:监控特定品类商品的价格、销量变化
智能推荐系统:基于用户搜索行为推荐相关商品
库存监控系统:实时跟踪特定商品的库存状态
开发者在实际应用中,应根据业务需求合理选择参数和优化策略,在保证性能的同时严格遵守平台规范,构建合规、高效的商品搜索系统。
