Формат помощи выбирают не по принципу «к...
注册账号免费测试1688API接口1688 的 item_review 接口(官方标准方法名:alibaba.item.review.get)是阿里巴巴开放平台提供的核心接口之一,用于获取指定商品的买家评论数据,包括文本评价、评分、晒图、采购属性(如采购数量、...
还有 84 人发表了评论 加入10667人围观注册账号免费测试1688API接口1688 的 item_review 接口(官方标准方法名:alibaba.item.review.get)是阿里巴巴开放平台提供的核心接口之一,用于获取指定商品的买家评论数据,包括文本评价、评分、晒图、采购属性(如采购数量、...
还有 84 人发表了评论 加入10667人围观# 淘宝开放平台 vs 1688 vs 京东宙斯:能力、权限、坑点全梳理(附 Python 源码)
这篇跟前几篇不一样:没有故事线,是一篇**查表文**。
它解决的是每个接入团队都会遇到的一个问题——三个平台的文档各说各话,你没办法把三份文档摊在一起比。这篇就是把它们摊在一起比完了的样子。
建议先收藏,用到哪条查哪条。文章末尾有可直接跑的签名与请求代码,以及一份能力声明表——那部分是能直接抄进项目里的。
---
## 一、三个平台,三种"人设"
动手之前先建立整体印象,后面查表才有方向:
**淘宝开放平台(TOP)**
年纪最大、接口最多、文档最全,也因此**历史包袱最重**。同一种数据往往有三四个接口能拿到,字段命名横跨三个时代的风格。它的沙箱环境是三家里最不靠谱的——这是它最大的坑。
**京东宙斯**
流程最"企业化":资质要求最严、回调要求 HTTPS + 备案域名、审核规范。回报是**类目属性 schema 三家里最完整**,做商品发布类需求它最省心。它的坑集中在参数格式和返回体结构上,都是小坑,但数量多。
**1688 开放平台**
唯一的 B2B。它没有"零售"概念:没有 SKU 库存、价格是区间、按起订量阶梯。**不能把它当第三个淘宝来接**——这是接 1688 最大的认知错误。它的坑集中在签名和图片上。
---
## 二、能力对照大表
这张表是全文的核心。✓ = 支持,△ = 部分支持或有条件,✗ = 不支持。
| 维度 | 淘宝 TOP | 京东宙斯 | 1688 |
| --------------- | ----------------------- | ----------------------------- | ----------------------- |
| **签名算法** | MD5 三明治 | MD5 三明治 | **HMAC-SHA1 + urlPath** |
| **时间戳(签名用)** | 秒,`yyyy-MM-dd HH:mm:ss` | 秒,同左 | **毫秒**,`_aop_timestamp` |
| **业务时间参数** | `yyyy-MM-dd HH:mm:ss` | `yyyy-MM-dd HH:mm:ss`,部分接口只到天 | 秒级时间戳 |
| **业务参数传递** | 平铺 query | 打包进 `360buy_param_json` | 平铺 query |
| **返回结构** | 平铺 | **按接口名嵌套,要剥壳** | 平铺 |
| **分页方式** | `page_no / page_size` | `page / pageSize` | `startIndex / pageSize` |
| **分页上限** | 100 / 页 | 100 / 页 | 部分接口 startIndex 有上限 |
| **商品详情** | ✓ | ✓ | ✓ |
| **SKU 级库存** | ✓ | △(部分接口才返回) | **✗(按起订量卖,无 SKU 库存)** |
| **订单列表** | ✓ | ✓ | ✓(分销单为主) |
| **物流轨迹** | ✓(增量接口 + 轨迹) | △(部分按天查) | △(以发货状态为主) |
| **消息推送(TMC 类)** | ✓ TMC | △ 部分消息 | ✗(基本靠拉) |
| **沙箱环境** | △ **长期不同步** | ✓ 相对可用 | ✗ |
| **图片外链** | ✓ 可外链 | ✓ 可外链 | △ **有防盗链** |
| **token 有效期** | 自用型长,授权型短 | 短 + refresh_token | 长 |
| **审核周期** | 1~3 周 | 1~2 周 | 3~7 天 |
| **限流模型** | appkey × 接口,双限 | 应用 × 接口,双限 | appkey 流量包 + QPS |
| **适合谁** | 淘系卖家、全域比价 | 京东 POP / 自营 | 分销、一件代发、选品 |
三个格子单独说一下,因为它们最容易被误判:
**1688 没有 SKU 库存**。这不是接口没开放,是业务模型不同——1688 的商品按"起订量 → 单价"的区间卖,压根没有"某个 SKU 剩几件"的概念。你在第 8 篇见过的 `NotSupported` 异常,主要就是为它准备的。硬要接的话,能拿到的是"可售状态 + 阶梯价",拿不到库存数。
**淘宝沙箱长期不同步**。新接口没有沙箱、旧接口沙箱返回的字段和线上不一致,这两件事都是常态。所以我们的冒烟测试打的是线上真实接口(用固定样本、极低频率),沙箱只用来验证签名和连通性。**别信沙箱的返回体**。
**1688 图片有防盗链**。商品图直接 `<img src>` 塞进自己的页面,会有一部分图挂掉(referer 校验)。转存的时候要么带 Referer 伪造,要么走它提供的图片网关——前者不稳定,后者要申请。这个坑在第 2 篇的图片转存里埋过,这里说清楚原因。
---
## 三、权限申请对照:代码之外花时间最多的事
第 6 篇说过:代码三天写得完,权限可能等三周。这里把三个平台的申请路径摊开。
| 环节 | 淘宝 TOP | 京东宙斯 | 1688 |
| ------- | ------------------ | ---------------- | ----------------- |
| 主体要求 | 企业/个人开发者入驻 | **企业资质**(营业执照等) | 企业认证 |
| 应用类型 | 自用型 / 他用型(ISV) | 自用 / 工具型 | 自用应用 / 他用 |
| 接口权限 | 按**权限包**申请,敏感接口单独审 | 控制台勾选 + 部分白名单 | 权限包,B2B 类目相关要单独申请 |
| 回调要求 | 域名备案 | **HTTPS + 备案域名** | HTTPS |
| 最容易卡的地方 | "他用型"的场景说明要写清楚 | 资质材料一次过审的很少 | 类目权限漏申请 |
三条实战建议:
1. **权限包按"最终要调的接口"倒推**,不是按"类目"申请。淘宝的权限包和接口是多对多关系,最容易犯的错是以为申请了商品权限包就能调所有商品接口——`item.seller.get` 和 `items.list.get` 在不同的包里。
2. **场景说明写具体业务**。"数据同步"会被打回,"为本店已成交订单同步物流轨迹用于售后跟踪"能过。审核看的是你会不会拿数据干别的。
3. 三个平台的 token 机制不一样,**自用型和授权型混着用迟早出事**。自用型 token 长期有效但只能操作自己的店;授权型短,要走 refresh。如果你的产品要服务多家店,从第一天就按授权型设计,别混。
---
## 四、签名:三个平台,三种脾气
签名是接入期最先撞上的东西,也是最值得写成代码的部分。三家的算法其实是"一个思路 + 两个变体":
- 淘宝、京东:`md5(secret + k1v1k2v2... + secret)`,**几乎一样**
- 1688:`hmac_sha1(secret, urlPath + k1v1k2v2...)`,**urlPath 也参与签名**,且时间戳是毫秒
```python
# signers.py
import hashlib
import hmac
def _sorted_concat(params: dict) -> str:
"""k1v1k2v2...,按 key 的 ASCII 排序——三平台一致的要求"""
return "".join(f"{k}{params[k]}" for k in sorted(params))
def sign_taobao(secret: str, params: dict) -> str:
"""TOP:md5(secret + 拼接 + secret),大写"""
raw = secret + _sorted_concat(params) + secret
return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()
def sign_jd(secret: str, params: dict) -> str:
"""宙斯:算法同 TOP,但业务参数打包在 360buy_param_json 里一起签"""
raw = secret + _sorted_concat(params) + secret
return hashlib.md5(raw.encode("utf-8")).hexdigest().upper()
def sign_alibaba(secret: str, url_path: str, params: dict) -> str:
"""
1688:hmac-sha1(secret, urlPath + 拼接),大写。
两个独特点:
1. urlPath 参与签名——漏了它,签名永远对不上,而且报错信息看不出原因
2. 时间戳用毫秒(_aop_timestamp),用秒的话签名同样对不上
"""
raw = url_path + _sorted_concat(params)
return hmac.new(secret.encode("utf-8"), raw.encode("utf-8"),
hashlib.sha1).hexdigest().upper()
```
签名调试有一个万能技巧,比任何文档都有用:**把待签字符串打出来,和平台报错页给的"期望签名"对比**。三个平台的签名错误报错都一样没用(就一句 `sign error`),唯一的调试方法就是对着拼。我们第 6 篇说"签名预估 0.5 天实际 1 天",多出来的半天基本都耗在这里。
---
## 五、三个客户端:把平台的怪脾气消化在适配器里
签名之上是请求。三个平台的"怪脾气"——京东的参数打包、京东的返回剥壳、淘宝的错误藏 body、1688 的毫秒时间戳——都在这一层消化掉,业务层不该看见它们。
```python
# clients.py
import json
import time
import httpx
from signers import sign_taobao, sign_jd, sign_alibaba
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class PlatformError(Exception):
def __init__(self, code, msg, platform):
super().__init__(f"[{platform}] {code}: {msg}")
self.code = str(code)
self.msg = msg
self.platform = platform
# ---------- 淘宝 TOP ----------
async def taobao_call(client: httpx.AsyncClient, cfg: dict, session: str,
method: str, api_params: dict) -> dict:
p = {
"method": method,
"app_key": cfg["app_key"],
"session": session,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"format": "json",
"v": "2.0",
"sign_method": "md5",
**{k: str(v) for k, v in api_params.items()},
}
p["sign"] = sign_taobao(cfg["app_secret"], p)
r = await client.get("https://gw.api.taobao.com/router/rest", params=p)
data = r.json()
# TOP 的 HTTP 永远 200,错误藏在 body 的 error_response 里
# ——只看状态码,监控永远是绿的(第 9 篇的口径问题就出在这)
if "error_response" in data:
err = data["error_response"]
raise PlatformError(err.get("code"), err.get("msg"), "taobao")
return data
# ---------- 京东宙斯 ----------
async def jd_call(client: httpx.AsyncClient, cfg: dict, token: str,
method: str, body: dict) -> dict:
p = {
"method": method,
"app_key": cfg["app_key"],
"access_token": token,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"v": "2.0",
# 业务参数整体打包成一个 JSON 字符串,再作为普通参数参与签名
# 字段名大小写敏感:文档里的 snake_case 只是显示,实际要 camelCase
"360buy_param_json": json.dumps(
body, ensure_ascii=False, separators=(",", ":")
),
}
p["sign"] = sign_jd(cfg["app_secret"], p)
r = await client.get("https://api.jd.com/routerjson", params=p)
return jd_unwrap(r.json(), method)
def jd_unwrap(data: dict, method: str) -> dict:
"""
宙斯的返回体按接口名嵌套一层,且不同网关下 key 的写法不一致
(点号 / 下划线都见过),做兼容处理。
这是京东接入手册里最常被问的一句话:"返回怎么多包了一层"
"""
if "errorResponse" in data:
err = data["errorResponse"]
raise PlatformError(err.get("code"), err.get("zh_desc"), "jd")
for key in (method, method.replace(".", "_")):
if key in data:
inner = data[key]
# 部分接口在剥完第一层后还有个 result 包裹,一并处理
if isinstance(inner, dict) and "result" in inner:
return inner["result"]
return inner
raise PlatformError("UNWRAP", f"返回体里找不到 {method}", "jd")
# ---------- 1688 ----------
async def alibaba_call(client: httpx.AsyncClient, cfg: dict,
url_path: str, api_params: dict) -> dict:
p = {
**{k: str(v) for k, v in api_params.items()},
"_aop_key": cfg["app_key"],
# 毫秒!用秒的话签名对不上,而且报错不说原因
"_aop_timestamp": str(int(time.time() * 1000)),
}
p["_aop_signature"] = sign_alibaba(cfg["app_secret"], url_path, p)
# url_path 形如 /openapi/param2/1/com.alibaba.trade/alibaba.trade.get.buyerView
r = await client.get(f"https://gw.open.1688.com{url_path}", params=p)
data = r.json()
if not data.get("success", True):
err = data.get("error_info") or {}
raise PlatformError(err.get("code"), err.get("desc"), "alibaba")
result = data.get("result", data)
# 1688 有一种"软失败":success 是 true,但业务字段是空的。
# 对空数据做一次必填校验,别把空当成功传下去
if isinstance(result, dict) and result.get("datas") == []:
raise PlatformError("EMPTY", "业务字段为空的软失败", "alibaba")
return result
```
这段代码里有三个细节,是接入期最容易各自踩一遍的:
1. **京东的 `360buy_param_json` 要压缩**。`ensure_ascii=False` + `separators=(",", ":")` 不是洁癖——参数里有中文时,默认的 `ensure_ascii=True` 会把中文转成 `\uXXXX`,签名按转义后的算,服务端按原文验,**永远对不上**。这是京东接入报错里出现频率最高的一条。
2. **淘宝的错误码是 int 不是 str**。`err.get("code")` 拿到的是 `7` 不是 `"7"`,如果你的错误分类表按字符串写,一条都匹配不上。所以 `PlatformError` 里统一 `str(code)`。
3. **1688 的"软失败"要校验**。`success: true` 但 `datas: []` 的情况真实存在(商品下架、权限降级时会出现),直接当成功处理会让下游静默拿到空数据。
---
## 六、能力声明表:把第二节的表变成代码
第二节的表不该只存在于文章里。把它变成数据,业务层就能在启动时校验,而不是等到运行时才发现 1688 没有 SKU 库存:
```python
# capabilities.py
from enum import Enum
class Cap(str, Enum):
YES = "yes"
PARTIAL = "partial"
NO = "no"
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
MATRIX: dict[str, dict[str, Cap]] = {
"item.get": {"taobao": Cap.YES, "jd": Cap.YES, "alibaba": Cap.YES},
"sku.stock": {"taobao": Cap.YES, "jd": Cap.PARTIAL, "alibaba": Cap.NO},
"order.list": {"taobao": Cap.YES, "jd": Cap.YES, "alibaba": Cap.YES},
"logistics.trace": {"taobao": Cap.YES, "jd": Cap.PARTIAL, "alibaba": Cap.PARTIAL},
"push.tmc": {"taobao": Cap.YES, "jd": Cap.PARTIAL, "alibaba": Cap.NO},
"sandbox": {"taobao": Cap.PARTIAL, "jd": Cap.YES, "alibaba": Cap.NO},
}
class NotSupported(Exception):
pass
def check(platform: str, *needs: str) -> None:
"""启动时调一次。缺什么当场报,别等运行时"""
for need in needs:
cap = MATRIX.get(need, {}).get(platform)
if cap is None:
raise NotSupported(f"未知能力 {need}")
if cap is Cap.NO:
raise NotSupported(f"{platform} 不支持 {need}")
if cap is Cap.PARTIAL:
# partial 不算致命,但要写日志,提醒实现的人去看条件
print(f"[warn] {platform} 对 {need} 仅部分支持,先确认条件")
```
`check("alibaba", "sku.stock")` 会在启动那一刻就报 `alibaba 不支持 sku.stock`——比三个星期后客户投诉"库存显示不对"强得多。这套东西在第 8 篇展开过架构,这里给的是它的**数据来源**。
---
## 七、坑点清单(每平台五条,按踩的人次排序)
这部分建议直接转成团队的接入 checklist。
### 淘宝 TOP
| # | 坑 | 症状 | 解法 |
| - | --------------------- | -------------- | ---------------------- |
| 1 | 沙箱与线上不同步 | 沙箱全绿,上线全挂 | 冒烟打线上固定样本,沙箱只验签名 |
| 2 | `num_iid` 是长数字 | 前端/JS 侧 ID 丢精度 | 全链路按字符串传,落库也用 TEXT |
| 3 | 分页 `total_results` 不准 | 大类目翻页翻到一半开始重复 | 超 1 万条改用**时间窗切片**,不靠翻页 |
| 4 | TMC 消息重复 + 乱序 | 库存被旧消息覆盖 | 消息带版本号,旧的丢弃(幂等) |
| 5 | 错误码是 int | 错误分类表匹配不上 | 统一 `str(code)` 再分类 |
其中第 3 条值得展开一句:`page_no` 翻到几百页之后,`total_results` 会因为数据在变而漂移,翻页翻出重复或漏。**大结果的正确姿势永远是"时间窗 + 游标"**,这和第 3 篇讲订单增量的思路是同一个。
### 京东宙斯
| # | 坑 | 症状 | 解法 |
| --- | --- | --- | --- |
| 1 | `360buy_param_json` 中文转义 | 签名永远对不上 | `ensure_ascii=False` + 压缩分隔符 |
| 2 | 返回体按接口名嵌套 | "数据怎么多包了一层" | `jd_unwrap` 统一剥壳,兼容点号/下划线 |
| 3 | 字段大小写敏感 | 参数传了但没生效 | 文档里 snake_case 是给人看的,代码用 camelCase |
| 4 | 部分接口只支持按天 | 想查小时级查不了 | 按天拉 + 本地按秒过滤,别硬试 |
| 5 | 回调要 HTTPS + 备案 | 测试环境收不到推送 | 提前准备域名,这个等不起 |
### 1688
| # | 坑 | 症状 | 解法 |
| --- | --- | --- | --- |
| 1 | 签名带 urlPath + 毫秒时间戳 | 签名永远对不上,报错不说原因 | 用 `sign_alibaba`,别套淘宝的写法 |
| 2 | 没有 SKU 库存 | 拿到的"库存"其实是别的 | 按"可售状态 + 阶梯价"建模,别硬凑 |
| 3 | 图片防盗链 | 铺到自己页面图挂一半 | 转存走图片网关,或处理 Referer |
| 4 | startIndex 有上限 | 大供应商商品翻不全 | 改时间游标增量拉 |
| 5 | 软失败(success=true 但空数据) | 下游静默拿到空 | 对关键字段做非空校验 |
三张表放一起看,会发现一个规律:**淘宝的坑在"数据形态",京东的坑在"格式规范",1688 的坑在"业务模型"**。预判一个团队接哪个平台会卡多久,看他们擅长处理哪种问题就行。
---
## 八、选型:什么情况用谁
最后把三家的适用场景说透,免得这张表看着热闹,落不了地:
- **做淘系卖家工具、全域比价、竞品监控** → 淘宝 TOP 绕不开,沙箱的坑认了
- **给京东 POP 商家做上货、订单处理** → 京东宙斯,类目 schema 完整是最实在的优势
- **做分销、一件代发、选品比价** → 1688,且**从第一天就按"无 SKU 库存"设计数据模型**
- **三平台都要** → 别做三套代码。第 8 篇那套统一接口层 + 这篇的能力声明表,就是为这个场景准备的
以及一个提醒:三家的审核周期加起来比你的开发周期长。**第一天就把三个平台的权限申请提交了,再开始写代码**——这是第 6 篇 Day 0 清单里第一条的原因,这篇算是把 Day 0 的细节补全了。
---
## 九、最后
这篇的代码量是十篇里最小的,但查表密度最高。因为它回答的问题不是"怎么做",是"**差异在哪**"——而差异是接入期所有工时超支的根源。
第 6 篇里那句"签名预估 0.5 天实际 1 天",多出来的半天就在第四节;"错误处理预估 0.5 天实际 1 天",多出来的半天就是第五节那些剥壳和软失败。**把差异提前写下来,排期才敢往下压。**
我们是做这三平台数据接口服务的,这篇文章里的坑就是我们每天在替客户踩的。代码都是能直接跑的骨架,`signers.py` 和 `clients.py` 拿去改改配置就能用;`capabilities.py` 的矩阵按你们的接口清单补全。
下一篇打算写「多店铺授权管理」:token 的存储、刷新、并发刷新撞车、单店授权过期怎么隔离——那是这套接入体系的最后一个坑区。