《拼多多商品API:pdd.goods.detail.get 与 pdd.goods.cats.get 的类目与规格踩坑》(附 Python 源码)
拼多多商品模型里最坑的不是“拿不到数据”,而是类目有两套、规格是组合爆炸、cat_id 不能直接当发布类目、spec 不是 SKU。
pdd.goods.cats.get:标准类目树(全平台类目字典),不是“当前店铺能发什么”
pdd.goods.authorization.cats:店铺可发布类目(受经营范围限制)
pdd.goods.detail.get:商品视图,cat_id / cat_ids是归属类目,sku_list / goods_specs才是可售单元拼多多的“规格”是 维度(spec_key) × 值(spec_value) → SKU 组合,直接平铺就会下错“红色 / M”
一、两个接口的职责边界
接口 | 视角 | 主要用途 | 常见误用 |
|---|---|---|---|
pdd.goods.cats.get | 平台标准类目树 | 类目 ID ↔ 名称、层级、父子关系 | 拿它当“本店可发类目” |
pdd.goods.authorization.cats | 商家可发布类目 | 发布商品时选类目 | 忘了循环下钻到叶子 |
pdd.goods.detail.get | 单个商品详情 | 标题/价格/图/类目/规格/SKU | 以为 specs 就是可售 SKU |
标准类目用
pdd.goods.cats.get商家可发类目用
pdd.goods.authorization.cats要发商品必须循环下钻到最子集(叶子)类目,拼多多商品必须有三级或四级分类
二、类目踩坑 1:cat_id ≠ 可发布类目
pdd.goods.detail.get 返回:{
"cat_id": 12345,
"cat_ids": [100, 200, 12345]
}cat_id:商品当前归属类目(可能是三级/四级)cat_ids:一级 → 二级 → 三级 → 四级 路径
店铺能不能发这个类目,要看pdd.goods.authorization.cats。不在店铺经营范围 → 用cat_id调pdd.goods.add直接报:“当前商品分类不在店铺经营范围内”
authorization.cats(parent_cat_id=0)拿一级可发类目逐级下钻到叶子
再用叶子
cat_id发商品goods.cats.get只做“类目 ID → 名称 / 路径展示 / 跨平台映射”
三、类目踩坑 2:类目会到四级,不是淘宝三级就完事
pdd.goods.cats.get 返回结构:{
"goods_cats_get_response": {
"goods_cats_list": [
{
"cat_id": 239,
"cat_name": "男装",
"level": 1,
"parent_cat_id": 0
},
{
"cat_id": 242,
"cat_name": "T恤",
"level": 3,
"parent_cat_id": 241
}
]
}
}level=1一级,level=2二级,level=3三级,level=4四级有些类目三级是叶子,有些要四级
发布必须用叶子;用父级
cat_id发商品 → 审核挂起/类目校验失败
四、规格踩坑 3:specs 不是 SKU,是“维度值清单”
{
"goods_specs": [
{"spec_key": "颜色", "spec_value": "红色", "spec_id": 1},
{"spec_key": "颜色", "spec_value": "蓝色", "spec_id": 2},
{"spec_key": "尺码", "spec_value": "M", "spec_id": 3},
{"spec_key": "尺码", "spec_value": "L", "spec_id": 4}
],
"sku_list": [
{"sku_id": "111", "specs": [1, 3], "price": 3900, "quantity": 10},
{"sku_id": "112", "specs": [1, 4], "specs_info": [{"key":"颜色","value":"红色"},{"key":"尺码","value":"L"}], "price": 4100, "quantity": 8},
{"sku_id": "113", "specs": [2, 3], "price": 3900, "quantity": 5}
]
}options = ["红色", "蓝色", "M", "L"] # 用户选规格时下成 红色 + M 两个标签
goods_specs:规格维度值池sku_list[].specs:该 SKU 引用的 spec_id 组合真正下单/库存/价格单位是
sku_id
五、Python:类目树加载器
# pdd/category_tree.py
from typing import Optional
class PddCategoryTree:
def __init__(self, client):
self.client = client
self.cache = {} # cat_id -> node
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def load_children(self, parent_cat_id: int = 0):
resp = self.client.execute("pdd.goods.cats.get", {
"parent_cat_id": parent_cat_id,
})
nodes = (
resp.get("goods_cats_get_response", {})
.get("goods_cats_list", [])
)
for n in nodes:
self.cache[n["cat_id"]] = n
return nodes
def load_full_path(self, cat_ids: list[int]):
"""cat_ids=[100,200,12345] -> 名称路径"""
return [self.cat_name(cid) for cid in cat_ids]
def cat_name(self, cat_id: int) -> Optional[str]:
if cat_id in self.cache:
return self.cache[cat_id]["cat_name"]
# 没缓存就查一次
children = self.load_children(0)
return self.cache.get(cat_id, {}).get("cat_name")
def is_leaf(self, cat_id: int) -> bool:
children = self.load_children(cat_id)
return len(children) == 0def shop_publishable_leaf_cats(client):
leaves = []
def dfs(parent):
nodes = client.execute("pdd.goods.authorization.cats", {
"parent_cat_id": parent
}).get("goods_auth_cats_get_response", {}).get("goods_cats_list", [])
if not nodes:
leaves.append(parent)
return
for n in nodes:
dfs(n["cat_id"])
dfs(0)
return leaves六、Python:规格 → SKU 组合解析
# pdd/spec_resolver.py
from dataclasses import dataclass
from typing import List, Dict
@dataclass
class ResolvedSku:
sku_id: str
spec_combo: Dict[str, str] # {"颜色": "红色", "尺码": "M"}
price_yuan: float
stock: int
def build_spec_index(goods_specs: list) -> Dict[int, dict]:
"""
spec_id -> {key, value}
"""
idx = {}
for s in goods_specs or []:
idx[s["spec_id"]] = {
"key": s.get("spec_key"),
"value": s.get("spec_value"),
}
return idx
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def resolve_skus(goods_detail: dict) -> List[ResolvedSku]:
specs = goods_detail.get("goods_specs", [])
spec_idx = build_spec_index(specs)
out = []
for sku in goods_detail.get("sku_list", []):
combo = {}
for sid in sku.get("specs", []):
meta = spec_idx.get(sid)
if meta:
combo[meta["key"]] = meta["value"]
# 拼多多价格单位:分
price_fen = sku.get("price") or sku.get("sku_price") or 0
stock = sku.get("quantity") or sku.get("sku_quantity") or 0
out.append(ResolvedSku(
sku_id=str(sku["sku_id"]),
spec_combo=combo,
price_yuan=price_fen / 100.0,
stock=int(stock),
))
return outskus = resolve_skus(detail)
for s in skus:
print(s.sku_id, s.spec_combo, s.price_yuan, s.stock)
# 111 {'颜色': '红色', '尺码': 'M'} 39.0 10sku_list[].specs_info=[{"key":"颜色","value":"红色"}],specs=[spec_id,...]。七、跨平台规格归一(接前几篇)
平台 | 规格模型 | 可售主键 |
|---|---|---|
1688 | skuInfo / skuArray,属性对 | offerId + skuId |
淘宝 | skus.sku.properties_name = pid:vid:名:值 | num_iid + sku_id |
京东 | skuList[].specText | skuId + skuList.skuId |
拼多多 | goods_specs 维度值 + sku_list.specs 引用 | goods_id + sku_id |
{
"platform": "pdd",
"spu_id": goods_id,
"sku_id": sku_id,
"attrs": {"颜色": "红色", "尺码": "M"},
"price": 39.00,
"currency": "CNY",
"stock": 10,
"stock_trusted": True,
}跨平台别用“颜色/尺码”硬字段
用
attrs: dict+sku_fingerprint = sha1(json(sorted(attrs)))
八、类目映射踩坑:用 cat_id 跨平台直连会翻车
cid3=50012029 ≠ 京东 cid3=123 ≠ 拼多多 cat_id=12345各平台拉类目树
建
platform_cat_id ↔ 标准类目词(女装/T恤/3C)用“类目路径文本 + 属性词”做向量/规则匹配
低置信度 → 人工审
def pdd_cat_path(client, cat_ids): tree = PddCategoryTree(client) return " > ".join(tree.cat_name(c) for c in cat_ids) # "男装 > T恤 > 短袖T恤"
九、发布商品时的类目/规格顺序(很重要)
1. authorization.cats 下钻到叶子 cat_id 2. goods.cat.rule.get 拿类目必填属性 3. goods.spec.get / goods.spec.id.get 建规格维度 4. 组装 sku_list:spec 组合 → 价格 → 库存 5. goods.img.upload 传图 6. goods.add / goods.edit.goods.commit 7. goods.commit.status.get 查审核
用标准类目
cats.get的 id 直接发 → 不在经营范围用父级类目发 → 类目校验失败
只传
specs不传sku_list→ 没可售单元把“颜色:红”和“尺码:M”当两个 SKU → 下单串规格
十、字段缺失清单(拼多多特有)
cat_ids可能空 → 用cat_id反查,但别信它完整goods_desc可能带 HTML / 被审核截断min_group_price / min_normal_price单位是分sku_list可能空(仅 SPU 无 SKU 的商品)多多进宝接口
pdd.ddk.goods.detail返回结构和商家接口不同,别混 parsergoods_sign是加密商品标识,goods_id在某些场景会被引导用 goods_sign 替代
十一、和前几篇收口
1688:
offerId+skuId,批发视角,库存快照不可信淘宝:
item.get公开 /item.seller.get私有,SKU 是 properties_name京东:
jingdong.item.read.get是 SPU+skuList+priceInfo+stockInfo拼多多:
goods_specs是维度值池,sku_list.specs才是组合,类目有“标准类目 / 可发类目”两套
淘宝把 SKU 藏在properties_name里,京东把 SKU 放在skuList.specText里,拼多多把 SKU 拆成 spec 维度再让你自己笛卡尔——谁直接把颜色:红, 尺码:M当两个下拉项,谁就会把“红色M”卖成“红色 + M”。