1688物流跟踪API:实时查询快递轨迹对接方案(附python源码)[特殊字符] 1688物流跟踪API:实时查询快递轨迹对接方案(附Python源码)
1688的物流跟踪主要通过两个接口完成:① 查询订单发货物流信息(alibaba.logistics.trade.ship/alibaba.logistics.order.get)和② 订阅/解析运单轨迹(alibaba.logistics.trace.get)。对于ERP/WMS系统,核心目标是:拿到1688发货的运单号 → 定时拉取物流轨迹 → 回写ERP出库单状态。
一、1688物流对接的两个核心接口
接口 | 用途 | 关键返回 |
|---|---|---|
| 查某采购单的发货记录 |
|
| 根据 | 签收状态、节点时间、当前城市 |
⚠️前提:应用需申请
物流查询权限(自用型应用默认可申请),订单须是已发货状态才有数据。
二、Python封装:查运单号 + 拉取轨迹
# ali1688_logistics.py import hashlib import time import requests import urllib.parse from typing import Dict, List, Optional from datetime import datetime, timedelta # 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex class Ali1688LogisticsClient: """ 1688 物流跟踪 Client 网关与签名规则同标准1688 Open API """ GATEWAY = "https://gw.open.1688.com/openapi/http/2/1" def __init__(self, app_key: str, app_secret: str, access_token: str): self.app_key = app_key self.app_secret = app_secret self.access_token = access_token # ─────────────── 签名(MD5) ─────────────── def _sign(self, params: Dict) -> str: filtered = sorted((k, v) for k, v in params.items() if v is not None) qs = ''.join(f"{k}{v}" for k, v in filtered) raw = f"{self.app_secret}{qs}{self.app_secret}" return hashlib.md5(raw.encode('utf-8')).hexdigest().upper() def _call(self, method: str, biz: Dict) -> Dict: api_params = { "method": method, "app_key": self.app_key, "session": self.access_token, "timestamp": str(int(time.time() * 1000)), "format": "json", "v": "2.0", "sign_method": "md5", } api_params["param2"] = urllib.parse.quote_plus( str(biz).replace("'", '"') ) api_params["sign"] = self._sign(api_params) resp = requests.get(self.GATEWAY, params=api_params, timeout=15) resp.raise_for_status() data = resp.json() if "error_response" in data: err = data["error_response"] raise Exception(f"1688 Logistics Err[{err.get('code')}]: {err.get('msg')}") result_key = [k for k in data if k != "error_response"][0] return data[result_key] # ─────────────── ① 查订单发货物流 ─────────────── def get_order_logistics(self, order_id: str) -> List[Dict]: """ 返回该订单下所有物流单 每个元素含: logisticsCode(快递码), billNo(运单号), companyName """ biz = {"orderId": order_id} res = self._call("alibaba.logistics.trade.ship", biz) orders = res.get("logisticsOrders", []) or [] result = [] for lo in orders: result.append({ "logistics_code": lo.get("logisticsCode"), # 如 "YTO" "SF" "logistics_name": lo.get("logisticsCompanyName"), "bill_no": lo.get("billNo") or lo.get("mailNo"), "send_time": lo.get("gmtSend"), "consignee": lo.get("consigneeName") }) return result # ─────────────── ② 查运单轨迹 ─────────────── def get_trace(self, company_code: str, bill_no: str) -> Dict: """ company_code: 1688返回的 logisticsCode (YTO/ZJS/SF...) bill_no: 运单号 返回含 signStatus(已签/未签) + traceList """ biz = { "companyCode": company_code, "billNo": bill_no } res = self._call("alibaba.logistics.trace.get", biz) return { "sign_status": res.get("signStatus"), # SIGN 已签 / UNSIGN 未签 "sign_time": res.get("signTime"), "traces": res.get("traceList") or [] } # ========================================================= # 使用示例:同步1688采购单物流 → 回写ERP # ========================================================= if __name__ == "__main__": client = Ali1688LogisticsClient( app_key="YOUR_APP_KEY", app_secret="YOUR_APP_SECRET", access_token="YOUR_ACCESS_TOKEN" ) ORDER_ID = "2338123456789000" # 1688采购单号 try: # ① 获取运单 logistics = client.get_order_logistics(ORDER_ID) if not logistics: print("⚠️ 该订单尚未发货或无物流信息") exit() for lg in logistics: print(f"\n📦 {lg['logistics_name']} 运单:{lg['bill_no']}") # ② 查轨迹 trace = client.get_trace(lg["logistics_code"], lg["bill_no"]) print(f" 签收状态: {trace['sign_status']} {trace['sign_time'] or ''}") for node in (trace["traces"] or []): print(f" [{node.get('time')}] {node.get('desc')} {node.get('city','')}") # ③ ERP联动(伪代码) # if trace['sign_status'] == 'SIGN': # erp.mark_received(ORDER_ID, sign_time=trace['sign_time']) except Exception as e: print(f"❌ {e}")三、1688快递公司码(LogisticsCode)常见值
快递 | logisticsCode | 说明 |
|---|---|---|
圆通 | YTO | 最常用 |
申通 | STO | — |
中通 | ZTO | — |
韵达 | YD | — |
顺丰 | SF | 需买家寄付/月结 |
京东 | JD | — |
邮政EMS | EMS | — |
💡避坑:1688返回的
logisticsCode是平台内部简码,直接传给trace.get即可,不要自己映射汉字。
四、ERP侧同步策略建议
┌──────────────┐ 每30分钟轮询已发货未签收订单 │ 1688 已发货单│ ──────────────────────────────▶ └──────────────┘ │ get_order_logistics() get_trace() │ ┌───────────▼──────────┐ │ ERP出库单状态更新 │ │ • 运输中 → 显示轨迹 │ │ • SIGN → 标记已签收 │ │ • 异常节点 → 告警 │ └──────────────────────┘轮询频率:已发货未签收订单每30min查一次,签收后停止轮询
失败重试:物流接口偶发超时,指数退避重试3次
轨迹去重:按
(bill_no, time, desc)去重存储,避免重复写状态变更
五、高频避坑点
问题 | 原因 | 解决 |
|---|---|---|
返回空物流 | 订单未发货/ | 先判断订单 |
| 自己手填汉字快递名 | 必须用1688返回的 |
轨迹长期不更新 | 快递公司未回传 | 正常,按 |
限流429 | QPS过高 | 单应用 sleep≥0.2s 或令牌桶 QPS≤10 |
六、面试/方案一句话
1688物流对接 =用采购单ID调
logistics.trade.ship拿运单号 → 调logistics.trace.get拉轨迹 → 按sign_status回写ERP签收状态,注意只对已发货订单查询且用平台返回的logisticsCode。
需要我把物流定时同步任务(APScheduler/Celery Beat)或ERP签收回写SQL模板补给你吗?
