前言 美客多开放平台商品详情接口,广泛用于跨境 ERP、竞品监控、选品数据分析。网上多数示例只做单接口简单请求演示,忽略平台独有的业务特性:商品基础信息与长描述分属两个独立接口、拉美多国站点字段结构存在差异、币种符号重复容易造成统计错乱、变体 SKU 数据嵌套层级深。 本文跳出基础调用演示,围绕双接口联动拉取、多站点字段兼容、业务数据归一化、限流退避做封装,解决定时同步任务中常见的程序崩溃、数据错乱问题,给出可直接用于业务系统的 Python 实现。 核心实现代码 核心设计思路 双接口联动获取完整数据:商品长描述不包含在主详情接口,单独调用描述接口后合并结果,避免丢失商品详情文本。 多站点异构字段兼容:品牌信息放在 attributes 数组,不同站点数组下标不固定,通过属性 ID 匹配取值,拒绝硬编码下标,防止部分站点解析报错。 币种风险规避:输出同时保留 分层异常拦截:区分令牌过期、商品不存在、限流、网络异常,上层业务可以针对性做刷新令牌、跳过下架商品、延时重试逻辑,不会单条数据异常中断批量任务。 对接踩坑要点 item_id 前缀对应站点,不能拿 MLM 站点 ID 调用 MLA 站点,会直接返回 404。 available_quantity 为参考库存,公开接口无法拿到商家真实库存,不能用于下单逻辑。 批量同步时遇到 429 限流,需要增加延时,禁止无限制并发请求。 即使商品下架,部分场景 HTTP 状态码依旧 200,业务必须校验 status 字段判断商品状态。
import requests
import time
class MeliDetailClient:
def __init__(self, access_token):
self.token = access_token
self.base_url = "https://api.mercadolibre.com"
self.headers = {"Authorization": f"Bearer {self.token}"}
def fetch_item_base(self, item_id):
"""获取商品基础信息,处理各类HTTP异常"""
url = f"{self.base_url}/items/{item_id}"
try:
resp = requests.get(url, headers=self.headers, timeout=18)
if resp.status_code == 401:
return {"status":False,"err_type":"token_expire","msg":"授权令牌过期需要刷新"}
if resp.status_code == 404:
return {"status":False,"err_type":"item_miss","msg":"商品不存在或已下架"}
if resp.status_code == 429:
time.sleep(3)
return {"status":False,"err_type":"rate_limit","msg":"触发接口限流"}
resp.raise_for_status()
return {"status":True,"data":resp.json()}
except Exception as e:
return {"status":False,"err_type":"network_err","msg":str(e)}
def fetch_item_desc(self, item_id):
"""单独获取商品长描述接口"""
url = f"{self.base_url}/items/{item_id}/description"
resp = requests.get(url, headers=self.headers, timeout=15)
if resp.status_code == 200:
return resp.json().get("plain_text", "")
return ""
def parse_normalize(self, item_id):
"""双接口合并,字段归一化输出"""
base_res = self.fetch_item_base(item_id)
if not base_res["status"]:
return base_res
raw = base_res["data"]
desc_text = self.fetch_item_desc(item_id)
output = {
"item_id": raw.get("id"),
"site_id": raw.get("site_id"),
"title": raw.get("title", ""),
"status": raw.get("status"),
"price": float(raw.get("price", 0)),
"currency_id": raw.get("currency_id", ""),
"available_quantity": raw.get("available_quantity", 0),
"description": desc_text,
"brand": "",
"variants": [],
"pic_list": [pic.get("secure_url") for pic in raw.get("pictures", [])]
}
#解析品牌属性,适配不同站点属性数组异构
for attr in raw.get("attributes", []):
if attr.get("id") == "BRAND":
output["brand"] = attr.get("value_name", "")
#解析变体SKU
for var in raw.get("variations", []):
output["variants"].append({
"variant_id":var.get("id"),
"sku_price":float(var.get("price",0)),
"sku_stock":var.get("available_quantity",0)
})
return {"status":True,"data":output}
if __name__ == "__main__":
client = MeliDetailClient("你的access_token")
result = client.parse_normalize("MLM123456789")
print(result)price与currency_id,墨西哥、阿根廷货币符号同为$,依靠 currency_id 区分,避免报表统计价格错乱。