京东商品数据怎么获取?商品详情、价格与评价 API 教程
使用 Just One API 从京东商品 ID 获取商品详情、图片、当前价格与买家评价。包含价格原值保存、评价分页去重和 JSON 导出的 Python 示例。
从一个京东商品 ID 开始
做商品分析时,详情、价格和买家评价通常需要一起看。Just One API 的京东接口可以按已知商品 ID 分别获取这些数据,再用相同的 ID 关联保存。
本教程使用商品详情 V4、商品价格 V1 和商品评价 V2,保存一份商品资料、一次价格快照和最多两页评价。适合先验证选定商品的数据是否满足需求,再扩展到自己的商品清单。
准备商品 ID 和 API Token
从京东商品详情页确认商品 ID,并保留你选择的具体商品或规格对应的 ID。不要把活动页编号、店铺 ID 或整段商品链接直接作为 itemId。
注册 Just One API 后获取 API Token,通过商品详情 V4 在线文档免费测试。一个 Token 可用于多个平台接口,后续按量调用;各接口权限及免费测试次数以账户显示为准。
下面三个 GET 接口都需要 token 和 itemId。判断成功时同时检查 HTTP 状态和响应体的业务 code=0,再读取 data。
第一步:获取商品详情与图片
调用 /api/jd/get-item-detail/v4,传入已知 itemId。当前公开响应样例中的 data 包含 sku、raw_data 和 graphext_data,其中 graphext_data.detail_images 为详情图片列表,graphext_data.properties 为属性信息。
先保存完整响应,再根据你实际拿到的字段选择商品属性和图片。不同商品的属性完整程度可能不同;缺少某个属性时应保留缺失状态,不要把其他商品的值补过来。返回图片链接与下载图片文件是两个步骤,本示例只保存链接。
这里选择 V4 是为了演示同步请求流程。商品详情 V2在文档中说明使用后台任务管理的异步流程,不应直接套用本教程的同步代码。版本号代表接口版本差异,选型可以参考接口版本号说明。
第二步:保存当前价格原值
调用商品价格 V1,路径为 /api/jd/get-item-price/v1。当前公开样例的价格记录在 data.data 数组中,记录包含 good_id 和 price。
公开说明没有明确给出样例价格字段的单位,因此代码把整个价格记录原样保存,不自动除以 100,也不直接给原值加上“元”的标签。做跨商品比较前,应先确认单位、对应商品 ID 和价格口径。
输出文件同时保存请求的商品 ID 与 UTC 采集时间。这是当次获取的价格快照;若要研究价格变化,需要按自己的频率重复采集并保留历史快照,单次调用不会自动补齐历史价格。优惠券、会员身份和活动条件也可能影响价格比较,需要按实际返回确认。
第三步:分页获取买家评价
调用商品评价 V2,路径为 /api/jd/get-item-comments/v2,从 page=1 开始逐页获取。
当前公开样例的评价列表在 data.items,包括 recordId、text、rating、createdAt 和 variantName。data.totalPages 可辅助控制结束位置。
脚本使用商品 ID 加评价 recordId 去重,遇到空页、整页重复或已达到返回的总页数时停止,并额外限制最多两页。评价列表和汇总数的统计口径可能不同,不能把少量页面当作完整评价全集。保留规格信息也有助于避免把不同款式的体验混在一起。
Python 示例:导出详情、价格与评价
安装依赖,设置 Token 和你要查询的数字商品 ID。将两个占位值替换成自己的值;命令适用于 macOS 或 Linux。
python3 -m pip install requests
export JUSTONEAPI_TOKEN='your_token'
export JD_ITEM_ID='your_numeric_item_id'保存代码为 jd_sample.py,运行 python3 jd_sample.py。一份详情、一次价格、最多两页评价,合计不超过 4 次 API 请求。脚本不会自动重试,已经成功保存的响应会保留下来。
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import requests
BASE_URL = "https://api.justoneapi.com"
TOKEN = os.environ.get("JUSTONEAPI_TOKEN", "")
def call_api(path, **params):
try:
response = requests.get(
BASE_URL + path,
params={"token": TOKEN, **params},
timeout=120, allow_redirects=False,
)
except requests.RequestException:
raise RuntimeError("API network error or timeout") from None
if response.status_code != 200:
raise RuntimeError(f"API HTTP {response.status_code}")
try:
result = response.json()
except ValueError:
raise RuntimeError("API response is not JSON") from None
if not isinstance(result, dict) or result.get("code") != 0:
code = result.get("code") if isinstance(result, dict) else None
raise RuntimeError(f"API failed: code={code}; check the docs")
data = result.get("data")
if not isinstance(data, dict):
raise RuntimeError("Unexpected data shape; inspect the docs")
inner = data.get("base_resp")
if isinstance(inner, dict) and inner.get("status_code") not in (0, "0"):
raise RuntimeError("Platform response was unsuccessful")
return result
def save_json(folder, name, value):
(folder / name).write_text(
json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8"
)
def require_list(data, key):
value = data.get(key)
if not isinstance(value, list):
raise RuntimeError(f"Expected a list at {key}; inspect saved JSON")
return value
def output_folder(topic):
if not TOKEN or TOKEN == "your_token":
raise RuntimeError("Set JUSTONEAPI_TOKEN before running")
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
folder = Path(topic + "-output") / run_id
folder.mkdir(parents=True, exist_ok=False)
return folder
ITEM_ID = os.environ.get("JD_ITEM_ID", "")
MAX_REVIEW_PAGES = 2
def main():
if not ITEM_ID or not ITEM_ID.isdigit():
raise RuntimeError("Set JD_ITEM_ID to a numeric product ID")
folder = output_folder("jd")
detail = call_api("/api/jd/get-item-detail/v4", itemId=ITEM_ID)
save_json(folder, "product-detail.json", detail)
price = call_api("/api/jd/get-item-price/v1", itemId=ITEM_ID)
save_json(folder, "product-price.json", price)
rows = require_list(price["data"], "data")
save_json(folder, "price-snapshot.json", {
"requestedItemId": ITEM_ID,
"retrievedAt": datetime.now(timezone.utc).isoformat(),
"rawPriceRecords": rows,
})
reviews, seen = [], set()
for page in range(1, MAX_REVIEW_PAGES + 1):
result = call_api(
"/api/jd/get-item-comments/v2", itemId=ITEM_ID, page=str(page)
)
save_json(folder, f"reviews-{page}.json", result)
data = result["data"]
items = require_list(data, "items")
if not items:
break
added = 0
for item in items:
if not isinstance(item, dict):
raise RuntimeError("Unexpected review item")
rid = item.get("recordId")
if rid is None:
raise RuntimeError("Missing review recordId; inspect saved JSON")
key = (ITEM_ID, str(rid))
if key in seen:
continue
seen.add(key)
added += 1
reviews.append({
"itemId": ITEM_ID, "recordId": str(rid),
"text": item.get("text"), "rating": item.get("rating"),
"createdAt": item.get("createdAt"),
"variantName": item.get("variantName"),
})
total_pages = data.get("totalPages")
if added == 0 or (
isinstance(total_pages, int) and page >= total_pages
):
break
save_json(folder, "reviews.json", reviews)
print(f"Saved product data and {len(reviews)} reviews to {folder}")
if __name__ == "__main__":
try:
main()
except RuntimeError as error:
raise SystemExit(str(error)) from None把输出用于商品分析
每次运行在 jd-output 下建立独立时间目录。product-detail.json 和 product-price.json 是完整响应,price-snapshot.json 保存价格原值及采集时间,reviews.json 是去重后的评价列表;原始分页也单独保存。
整理商品清单时,建议把请求的商品 ID、响应中的商品标识、采集时间和来源接口版本一起保留。分析评价时,可以先按规格和时间分组,再查看用户反复提及的功能或问题,并保留原文用于复核。不要直接把采样评价占比当作所有买家的满意度。
常见问题
详情接口是否已经包含价格,不用另查?
详情与价格接口的返回结构不同。本教程把价格作为单独的采集步骤,方便保留其来源和时间。是否能使用某个详情字段替代价格接口,要根据实际响应及业务所需口径判断。
如何采集更多商品?
可以把单个 JD_ITEM_ID 换成自己的商品 ID 清单,为每个商品保存独立结果,并限制总请求数。先用少量商品确认字段和单位,再增加任务量。若添加重试,请参考接口调用失败后的重试与并发策略。
为什么接口成功但评价没有数据?
成功响应中的空列表与调用失败不同。先查看商品 ID、当前页码和原始响应,再判断是否结束分页。不要为了凑足页数无限重复请求同一页。
现在可以打开京东商品详情文档,用一个熟悉的商品测试返回字段,再接入你的商品分析流程。
继续使用 Just One API
登录 Dashboard 获取 Token,查看完整 API 文档,或打开 MCP GitHub 项目了解最新配置。