返回教程列表
教程2026年10月11日4 分钟阅读

京东商品数据怎么获取?商品详情、价格与评价 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。

bash
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 请求。脚本不会自动重试,已经成功保存的响应会保留下来。

python
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 项目了解最新配置。