首页 / 资讯中心 / 文章详情

《京东商品API:从 item.jd.get 迁移到 jingdong.item.read.get 的字段映射与踩坑》(附 Python 源码)

《京东商品API:从 item.jd.get 迁移到 jingdong.item.read.get 的字段映射与踩坑》(附 Python 源码) ★ FEATURED ARTICLE
《京东商品API从 item.jd.get 迁移到 jingdong.item.read.get 的字段映射与踩坑》附 Python 源码先拍结论京东这波迁移不是重构业务而是“宙斯 JOS → 京东商家开放平台”的命名/入口收敛。网关还是https://api.jd.com/routerjson签名还是secret kv(ASCII) secretAccessToken 逻辑不变真正要改的是三件事method从item.jd.get / jd.item.get / jingdong.ware.get换成jingdong.item.read.get返回根节点从item_get_response / jingdong_ware_get_response换成jingdong_item_read_get_response旧接口“单 SKU 平铺”新接口“SPU skuList priceInfo stockInfo”嵌套化老代码直接item[title]的迁移后必炸。一、老 vs 新先对齐接口身份维度老item.jd.get/jd.item.get新jingdong.item.read.get平台宙斯 JOS已融合到 open.jd.com京东商家开放平台定位商品详情只读旧命名商品基础信息只读标准化网关api.jd.com/routerjson同上签名MD5(secretkvsecret)同上一行不改入参sku_id/item_idfieldssku_id/sku_idsfields批量不支持或各版本不一支持sku_ids最多 20 个返回根item_get_responsejingdong_item_read_get_response结构平铺多goodsInfoList / priceInfo / imageInfo / skuList / stockInfo新应用申请维护期不鼓励官方推荐经验口径网关/签名/鉴权零改动解析层重写批量能力顺手用上。二、旧接口“脑内模型”为什么会写错老item.jd.get很多人写成{ item_get_response: { item: { sku_id: 100012345678, title: XX 笔记本, price: 4999.00, stock: 120, image: https://img10.360buyimg.com/... } } }于是老代码item resp[item_get_response][item] title item[title] price float(item[price]) stock int(item[stock])三、新接口真实结构简化但接近生产{ jingdong_item_read_get_response: { code: 0, message: success, result: { goodsInfoList: [ { skuId: 100012345678, title: XX 笔记本, shortTitle: 轻薄本, saleState: 1, priceInfo: { marketPrice: 5999.00, jdPrice: 4999.00, promotionPrice: 4799.00, currency: CNY }, imageInfo: { mainImg: https://img10.360buyimg.com/n1/..., imgList: [https://img10..., https://img10...] }, brand: { brandId: 123, brandName: Lenovo }, category: { cid1: 1, cid1Name: 电脑办公, cid2: 12, cid2Name: 笔记本, cid3: 123, cid3Name: 轻薄本 }, stockInfo: { stockNum: 120, stockStatus: available }, salesInfo: { totalSold: 3200, commentCount: 1450, goodRate: 97.2 }, skuList: [ { skuId: 10001234567801, specText: 16G512G, price: 4999.00, stock: 60 }, { skuId: 10001234567802, specText: 32G1T, price: 5799.00, stock: 60 } ] } ] } } }⚠️ 不同权限包返回的字段名会有差异有的返回jdPrice有的返回salePrice有的返回stockNum有的返回stock有的返回goodsInfoList联盟接口返回data下架商品saleState0价格/库存可能直接为空所以迁移第一原则别信字段名先打原始 JSON 建映射表。四、字段映射表ERP 落地用老 item.jd.get新 jingdong.item.read.get说明item.sku_idgoodsInfoList[].skuId最小销售单元item.item_id/ware_idSPU 需另查 / 用 sku 反推京东强 SKU 模型item.titlegoodsInfoList[].titleitem.sub_titleshortTitle不一定有item.pricepriceInfo.jdPrice当前京东价item.market_pricepriceInfo.marketPrice划线价item.promotion_pricepriceInfo.promotionPrice活动价item.stockstockInfo.stockNum/skuList[].stock区域库存才准item.img_urlimageInfo.mainImg360buy 图链有防盗链item.imagesimageInfo.imgListitem.brandbrand.brandNameitem.cidcategory.cid3三级类目item.sku_attrsskuList[].specText“颜色:黑;尺码:XL”item.desc新接口常不返回详情 HTML 要别的权限/接口item.seller_nickshopInfo.shopName商家接口才有item.outer_id商家后台字段新只读接口未必返自营运配编码要店铺商品接口跨平台统一模型里京东主键用jd_skuId不是 item_id1688 是offerIdskuId淘宝是num_iidsku_id京东是skuId skuList[].skuId。五、Python兼容老新的客户端# jd/client.py import hashlib import time import requests class JDClient: GATEWAY https://api.jd.com/routerjson def __init__(self, app_key, app_secret, access_tokenNone, timeout5): self.app_key app_key self.app_secret app_secret self.access_token access_token self.timeout timeout # 封装好API供应商demo urlhttps://console.open.onebound.cn/console/?iLex def _sign(self, params: dict) - str: flat {k: v for k, v in params.items() if v is not None and k ! sign} qs .join(f{k}{v} for k, v in sorted(flat.items())) raw f{self.app_secret}{qs}{self.app_secret} return hashlib.md5(raw.encode(utf-8)).hexdigest().upper() def execute(self, method: str, biz: dict): params { app_key: self.app_key, method: method, timestamp: str(int(time.time() * 1000)), format: json, v: 2.0, sign_method: md5, } if self.access_token: params[access_token] self.access_token params.update(biz) params[sign] self._sign(params) resp requests.post(self.GATEWAY, dataparams, timeoutself.timeout) return resp.json()六、Python旧→新适配层重点# jd/item_adapter.py from dataclasses import dataclass, field from typing import Optional dataclass class JDItem: sku_id: str spu_hint: Optional[str] None title: Optional[str] None short_title: Optional[str] None market_price: Optional[float] None sale_price: Optional[float] None promo_price: Optional[float] None main_img: Optional[str] None img_list: list[str] field(default_factorylist) brand_name: Optional[str] None cid3: Optional[str] None cid3_name: Optional[str] None total_stock: Optional[int] None on_sale: bool True skus: list[dict] field(default_factorylist) raw: dict field(default_factorydict) # 封装好API供应商demo urlhttps://console.open.onebound.cn/console/?iLex def _f(x): try: return float(x) except (TypeError, ValueError): return None def _i(x): try: return int(x) except (TypeError, ValueError): return None def parse_legacy_item(resp: dict) - JDItem: 老 item.jd.get item (resp.get(item_get_response) or {}).get(item) or {} return JDItem( sku_idstr(item.get(sku_id) or item.get(skuId)), titleitem.get(title), market_price_f(item.get(market_price)), sale_price_f(item.get(price)), promo_price_f(item.get(promotion_price)), main_imgitem.get(img_url) or item.get(image), total_stock_i(item.get(stock)), skus[{ sku_id: str(item.get(sku_id)), spec: , price: _f(item.get(price)), stock: _i(item.get(stock)), }], rawitem, ) def parse_new_item(resp: dict) - JDItem: 新 jingdong.item.read.get node resp.get(jingdong_item_read_get_response) or {} if str(node.get(code)) not in (0, , None) and result not in node: raise RuntimeError(node.get(message) or jd item read error) goods_list ( (node.get(result) or {}).get(goodsInfoList) or [] ) if not goods_list: raise RuntimeError(empty goodsInfoList) g goods_list[0] price_info g.get(priceInfo) or {} stock_info g.get(stockInfo) or {} image_info g.get(imageInfo) or {} category g.get(category) or {} return JDItem( sku_idstr(g.get(skuId)), titleg.get(title), short_titleg.get(shortTitle), market_price_f(price_info.get(marketPrice)), sale_price_f(price_info.get(jdPrice) or price_info.get(salePrice)), promo_price_f(price_info.get(promotionPrice)), main_imgimage_info.get(mainImg), img_listimage_info.get(imgList) or [], brand_name(g.get(brand) or {}).get(brandName), cid3str(category.get(cid3) or ), cid3_namecategory.get(cid3Name), total_stock_i(stock_info.get(stockNum) or stock_info.get(stock)), on_saleg.get(saleState, 1) 1, skus[{ sku_id: str(s.get(skuId)), spec: s.get(specText) or s.get(spec) or , price: _f(s.get(price)), stock: _i(s.get(stock)), } for s in (g.get(skuList) or [])], rawg, )调用侧client JDClient(APP_KEY, APP_SECRET, ACCESS_TOKEN) # 老 legacy parse_legacy_item(client.execute(item.jd.get, {sku_id: sku})) # 新 new parse_new_item(client.execute(jingdong.item.read.get, { sku_id: sku, fields: skuId,title,shortTitle,priceInfo,imageInfo,brand,category,stockInfo,skuList,salesInfo }))七、批量迁移红利别再单 SKU 狂打def batch_get(client, sku_ids: list[str]): if len(sku_ids) 20: raise ValueError(jingdong.item.read.get 单次最多 20 个) resp client.execute(jingdong.item.read.get, { sku_ids: ,.join(sku_ids), fields: skuId,title,priceInfo,stockInfo,imageInfo,skuList }) node resp.get(jingdong_item_read_get_response) or {} return (node.get(result) or {}).get(goodsInfoList) or []限流口径社区经验基础 2 QPS企业 5~50 QPS批量 20 个 ≠ 20 次免费仍按平台计费/限流模型走BUCKET TokenBucket(rate8) # 企业 10 的话留余量 def safe_batch(skus): for chunk in chunks(skus, 20): BUCKET.acquire() yield batch_get(client, chunk)八、迁移期双跑校验很重要def migrate_check(sku): old safe_parse(parse_legacy_item(client.execute(item.jd.get, {sku_id: sku}))) new safe_parse(parse_new_item(client.execute(jingdong.item.read.get, {sku_id: sku}))) diffs [] if old.title ! new.title: diffs.append((title, old.title, new.title)) if abs((old.sale_price or 0) - (new.sale_price or 0)) 0.01: diffs.append((price, old.sale_price, new.sale_price)) if (old.total_stock or 0) ! (new.total_stock or 0): diffs.append((stock, old.total_stock, new.total_stock)) return diffs规则标题/价格不一致 → 先查area区域价、查促销时间窗库存不一致 → 京东库存是区域可售库存不传area默认北京老接口有、新接口没有desc / outer_id / cost_price→ 不是丢了是权限域不同九、区域库存坑京东特有client.execute(jingdong.item.read.get, { sku_id: sku, area: 19_1607_3157_0, # 省_市_区_镇 fields: skuId,stockInfo,priceInfo })不传area价格用默认区域库存用平台默认仓配视角跨境/家电/冷链会明显失真淘宝/1688 很少强调“区域库存”京东 POP/自营必须传area。十、和前几篇收口1688offerIdskuId采购视角amountOnSale-1/999999不可信淘宝num_iid卖家视角item.get公开 /item.seller.get私有京东skuId是王jingdong.item.read.get是商家/POP 只读视图联盟jd.union.open.goods.detail.query是 CPS 视角别混用迁移本质京东不是“接口变了”是“返回从平铺商品变成 SPU/SKU 价格库存对象树”一句话老京东商品接口教你怎么“取一个商品”新jingdong.item.read.get教你怎么“理解一个京东 SPU哪些 SKU、各区多少钱、哪区有货”。迁移别只改 method要把“商品一条记录”的脑模型升级成“商品SPU 聚合 SKU 集合 区域库存”。
阅读完成 · 觉得有帮助?
咨询建站