在电商数据驱动决策的场景中,商品详情页数据的实时性与准确性直接影响业务效果。京东提供的标准化 API 接口,为合规采集商品数据提供了可靠通道。本文将从开发准备、接口调试到数据对接的全流程进行实战演示,完整呈现京东商品详情页实时数据采集与业务系统对接的技术实现。
一、全流程架构设计与开发准备
1.1 全流程架构
商品详情页数据采集与对接的完整流程可分为「接口层 - 解析层 - 存储层 - 对接层」四个核心环节,各环节职责如下:
- 接口层:负责京东 API 的认证、请求发送与响应接收
- 解析层:将 API 返回的原始数据转换为结构化业务数据
- 存储层:将结构化数据存入数据库,并通过缓存提升访问效率
- 对接层:提供标准化接口供业务系统(如 ERP、BI 工具)调用
1.2 开发环境准备
- 开发语言:Python 3.10(推荐,支持异步特性与类型注解)
- 依赖库:
requests(HTTP 请求)、pymysql(MySQL 交互)、redis(缓存)、fastapi(对接层 API 服务) - 数据库:MySQL 8.0(存储结构化数据)、Redis 6.2(缓存热点数据)
- 工具:Postman(API 调试)、PyCharm(开发 IDE)
1.3 京东开放平台配置
- 注册开发者账号,获取
api_key和api_secret(接口调用凭证) - 申请「商品详情查询」接口权限,记录接口文档中的字段说明
- 确认接口调用限制(如 QPS=10,日调用量 = 10 万次),避免触发限流
二、接口层开发:京东 API 调用实现
2.1 签名生成与参数处理
京东 API 采用签名验证机制,需按规则生成 sign 参数以确保请求合法性:
import hashlib
import time
from urllib.parse import urlencode
from typing import Dict
class JDAPIAuth:
"""京东API认证工具类,负责签名生成与参数处理"""
@staticmethod
def generate_sign(params: Dict[str, str], app_secret: str) -> str:
"""
生成签名:按参数名ASCII升序排序→拼接→MD5加密
:param params: 接口请求参数(不含sign)
:param app_secret: 应用密钥
:return: 签名字符串
"""
# 1. 按参数名ASCII升序排序
sorted_params = sorted(params.items(), key=lambda x: x[0])
# 2. 拼接为key=value&key=value格式
param_str = urlencode(sorted_params)
# 3. 追加app_secret并MD5加密(转大写)
sign_str = f"{param_str}{app_secret}"
return hashlib.md5(sign_str.encode("utf-8")).hexdigest().upper()
@staticmethod
def get_timestamp() -> str:
"""生成符合格式的时间戳(yyyy-MM-dd HH:mm:ss)"""
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
2.2 API 调用核心类
封装商品详情页数据采集的核心逻辑,支持超时重试与错误处理:
import requests
import json
from typing import Optional, Dict
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class JDGoodsAPIClient:
"""京东商品详情API客户端"""
def __init__(self, app_key: str, app_secret: str):
self.app_key = app_key
self.app_secret = app_secret
self.api_endpoint = "https://api.jd.com/routerjson" # API网关地址
self.auth = JDAPIAuth()
self.session = self._init_session() # 初始化带重试的会话
def _init_session(self) -> requests.Session:
"""初始化HTTP会话,配置重试策略"""
session = requests.Session()
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=0.5, # 重试间隔(0.5, 1, 2秒)
status_forcelist=[429, 500, 502, 503, 504] # 需要重试的状态码
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
return session
def fetch_goods_detail(self, ware_id: str) -> Optional[Dict]:
"""
调用API获取商品详情原始数据
:param ware_id: 商品ID(从商品URL提取,如100012345678)
:return: 原始数据字典,失败返回None
"""
# 1. 组装基础参数
base_params = {
"method": "jingdong.ware.detail.get", # 接口方法名
"app_key": self.app_key,
"timestamp": self.auth.get_timestamp(),
"v": "2.0", # 接口版本
"format": "json", # 返回格式
"ware_id": ware_id # 商品ID
}
# 2. 生成签名并添加到参数
base_params["sign"] = self.auth.generate_sign(base_params, self.app_secret)
# 3. 发送POST请求
try:
response = self.session.post(
url=self.api_endpoint,
data=base_params,
timeout=10 # 超时时间10秒
)
response.raise_for_status() # 抛出HTTP错误(如401、500)
except requests.exceptions.RequestException as e:
print(f"API请求失败:{str(e)}")
return None
# 4. 解析响应数据
try:
result = json.loads(response.text)
# 京东API返回格式:{"jingdong_ware_detail_get_response": {"result": {...}}}
response_key = "jingdong_ware_detail_get_response"
if response_key in result:
return result[response_key].get("result", {})
else:
print(f"API返回格式异常:{result}")
return None
except json.JSONDecodeError as e:
print(f"JSON解析失败:{str(e)}")
return None
三、解析层开发:原始数据结构化处理
API 返回的原始数据字段繁多,需提取核心业务字段并转换为结构化格式:
from typing import Dict, List, Optional
class GoodsDataParser:
"""商品数据解析器,将原始API数据转换为业务结构化数据"""
@staticmethod
def parse(raw_data: Dict) -> Optional[Dict]:
"""
解析原始数据,提取核心字段
:param raw_data: API返回的原始数据
:return: 结构化商品信息
"""
if not raw_data:
return None
# 提取基础信息
base_info = raw_data.get("base", {})
# 提取价格信息(支持多价格类型)
price_info = raw_data.get("price", {})
# 提取库存信息
stock_info = raw_data.get("stock", {})
# 提取SKU信息
sku_list = raw_data.get("skuList", [])
# 结构化转换
return {
"goods_id": base_info.get("wareId", ""), # 商品ID
"goods_name": base_info.get("wareName", ""), # 商品名称
"brand": base_info.get("brandName", ""), # 品牌名称
"main_image": base_info.get("mainImgUrl", ""), # 主图URL
"jd_price": price_info.get("jdPrice", {}).get("price", 0.0), # 京东价
"market_price": price_info.get("marketPrice", {}).get("price", 0.0), # 市场价
"stock_num": stock_info.get("stockNum", 0), # 库存数量
"is_in_stock": stock_info.get("isStock", False), # 是否有货
"category": base_info.get("category", {}).get("categoryName", ""), # 所属分类
"sku_count": len(sku_list), # SKU数量
"skus": [ # SKU列表
{
"sku_id": sku.get("skuId", ""),
"sku_name": sku.get("skuName", ""),
"sku_price": sku.get("jdPrice", {}).get("price", 0.0)
} for sku in sku_list
],
"update_time": JDAPIAuth.get_timestamp() # 数据更新时间
}
四、存储层开发:数据持久化与缓存
4.1 数据库表设计
创建 MySQL 表存储商品详情数据(示例 SQL):
CREATE TABLE `jd_goods_detail` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`goods_id` varchar(20) NOT NULL COMMENT '商品ID',
`goods_name` varchar(255) NOT NULL COMMENT '商品名称',
`brand` varchar(100) DEFAULT '' COMMENT '品牌名称',
`main_image` varchar(512) DEFAULT '' COMMENT '主图URL',
`jd_price` decimal(10,2) DEFAULT 0.00 COMMENT '京东价',
`market_price` decimal(10,2) DEFAULT 0.00 COMMENT '市场价',
`stock_num` int DEFAULT 0 COMMENT '库存数量',
`is_in_stock` tinyint(1) DEFAULT 0 COMMENT '是否有货(1是0否)',
`category` varchar(100) DEFAULT '' COMMENT '所属分类',
`sku_count` int DEFAULT 0 COMMENT 'SKU数量',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_goods_id` (`goods_id`) COMMENT '商品ID唯一索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='京东商品详情表';
4.2 数据存储与缓存实现
import pymysql
import redis
import json
from typing import Dict, Optional
from pymysql.cursors import DictCursor
class GoodsDataStorage:
"""商品数据存储管理器,负责数据库存储与缓存"""
def __init__(self, mysql_config: Dict, redis_config: Dict):
# 初始化MySQL连接
self.mysql_conn = pymysql.connect(
host=mysql_config["host"],
user=mysql_config["user"],
password=mysql_config["password"],
database=mysql_config["db"],
port=mysql_config.get("port", 3306),
cursorclass=DictCursor
)
# 初始化Redis连接
self.redis_client = redis.Redis(
host=redis_config["host"],
port=redis_config.get("port", 6379),
password=redis_config.get("password", ""),
db=redis_config.get("db", 0),
decode_responses=True # 自动解码为字符串
)
def save_to_mysql(self, data: Dict) -> bool:
"""
保存结构化数据到MySQL(存在则更新,不存在则插入)
:param data: 结构化商品数据
:return: 操作是否成功
"""
if not data.get("goods_id"):
print("商品ID为空,跳过存储")
return False
sql = """
INSERT INTO jd_goods_detail (
goods_id, goods_name, brand, main_image, jd_price, market_price,
stock_num, is_in_stock, category, sku_count, update_time
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
goods_name = VALUES(goods_name),
brand = VALUES(brand),
main_image = VALUES(main_image),
jd_price = VALUES(jd_price),
market_price = VALUES(market_price),
stock_num = VALUES(stock_num),
is_in_stock = VALUES(is_in_stock),
category = VALUES(category),
sku_count = VALUES(sku_count),
update_time = VALUES(update_time)
"""
try:
with self.mysql_conn.cursor() as cursor:
cursor.execute(sql, (
data["goods_id"],
data["goods_name"],
data["brand"],
data["main_image"],
data["jd_price"],
data["market_price"],
data["stock_num"],
data["is_in_stock"],
data["category"],
data["sku_count"],
data["update_time"]
))
self.mysql_conn.commit()
print(f"商品[{data['goods_id']}]已保存到MySQL")
return True
except Exception as e:
self.mysql_conn.rollback()
print(f"MySQL存储失败:{str(e)}")
return False
def cache_to_redis(self, goods_id: str, data: Dict, expire=3600) -> bool:
"""
缓存数据到Redis(默认1小时过期)
:param goods_id: 商品ID
:param data: 结构化商品数据
:param expire: 过期时间(秒)
:return: 操作是否成功
"""
try:
self.redis_client.set(
f"jd_goods:{goods_id}",
json.dumps(data, ensure_ascii=False),
ex=expire
)
print(f"商品[{goods_id}]已缓存到Redis")
return True
except Exception as e:
print(f"Redis缓存失败:{str(e)}")
return False
def close(self):
"""关闭数据库连接"""
self.mysql_conn.close()
self.redis_client.close()
五、对接层开发:业务系统接口服务
使用 FastAPI 提供标准化接口,供业务系统(如 ERP、数据分析平台)调用:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional, Dict
# 初始化FastAPI应用
app = FastAPI(title="京东商品详情对接API")
# 全局存储实例(实际应用中建议用依赖注入)
storage = None # 需在启动时初始化:storage = GoodsDataStorage(...)
api_client = None # 需在启动时初始化:api_client = JDGoodsAPIClient(...)
parser = GoodsDataParser()
# 数据模型定义(API返回格式)
class GoodsDetailResponse(BaseModel):
code: int = 200
message: str = "success"
data: Optional[Dict] = None
@app.get("/goods/detail", response_model=GoodsDetailResponse)
def get_goods_detail(ware_id: str):
"""
业务系统对接接口:获取商品详情(优先从缓存获取,缓存失效则调用API)
:param ware_id: 商品ID
:return: 结构化商品数据
"""
# 1. 尝试从Redis缓存获取
cached_data = storage.redis_client.get(f"jd_goods:{ware_id}")
if cached_data:
return {"data": json.loads(cached_data)}
# 2. 缓存失效,调用京东API获取
raw_data = api_client.fetch_goods_detail(ware_id)
if not raw_data:
raise HTTPException(status_code=500, detail="获取商品数据失败")
# 3. 解析数据
parsed_data = parser.parse(raw_data)
if not parsed_data:
raise HTTPException(status_code=500, detail="数据解析失败")
# 4. 存储并缓存
storage.save_to_mysql(parsed_data)
storage.cache_to_redis(ware_id, parsed_data)
return {"data": parsed_data}
# 启动命令:uvicorn main:app --host 0.0.0.0 --port 8000
六、全流程演示与测试
6.1 流程串联执行
def run_full_process(ware_id: str):
"""执行商品数据采集-解析-存储全流程"""
# 配置参数(替换为实际值)
config = {
"app_key": "your_app_key",
"app_secret": "your_app_secret",
"mysql": {
"host": "localhost",
"user": "root",
"password": "123456",
"db": "jd_goods_db"
},
"redis": {
"host": "localhost",
"port": 6379
}
}
# 初始化组件
api_client = JDGoodsAPIClient(config["app_key"], config["app_secret"])
storage = GoodsDataStorage(config["mysql"], config["redis"])
# 1. 调用API获取原始数据
raw_data = api_client.fetch_goods_detail(ware_id)
if not raw_data:
print("流程终止:API获取数据失败")
return
# 2. 解析数据
parsed_data = GoodsDataParser.parse(raw_data)
if not parsed_data:
print("流程终止:数据解析失败")
return
# 3. 存储与缓存
storage.save_to_mysql(parsed_data)
storage.cache_to_redis(ware_id, parsed_data)
# 4. 关闭连接
storage.close()
print("全流程执行完成")
if __name__ == "__main__":
# 测试商品ID(替换为实际存在的商品ID)
test_ware_id = "100012345678"
run_full_process(test_ware_id)
6.2 测试验证
- 接口调用测试:运行
run_full_process函数,检查控制台输出是否显示 “全流程执行完成” - 数据库验证:登录 MySQL,查询
jd_goods_detail表,确认存在测试商品 ID 的数据 - 缓存验证:使用
redis-cli执行GET jd_goods:100012345678,确认返回 JSON 数据 - 对接接口测试:启动 FastAPI 服务,访问
http://localhost:8000/goods/detail?ware_id=100012345678,验证返回数据是否正确
七、总结与扩展建议
本文完整演示了京东商品详情页实时数据采集与对接的全流程,从 API 调用、数据解析、存储到业务对接,覆盖了技术实现的核心环节。关键技术点包括:
- 基于签名的 API 身份认证确保请求安全
- 结构化解析实现原始数据到业务数据的转换
- 数据库 + 缓存的存储策略平衡实时性与性能
- 标准化对接接口降低业务系统集成成本
扩展建议:
- 增加定时任务(如
APScheduler),定期更新商品数据 - 实现批量采集功能,支持多商品 ID 并发处理
- 增加监控告警(如失败次数超限通知)
- 对接消息队列(如 Kafka),实现数据异步处理
通过该方案,企业可合规、高效地获取京东商品详情页实时数据,为电商运营决策提供数据支撑。

700

被折叠的 条评论
为什么被折叠?



