×

义乌购商品 ID 详情接口实战:双算法兼容签名 + 小商品批发结构化解析 + 限流熔断(Python 合规生产版)

Ace Ace 发表于2026-06-24 16:59:46 浏览11 评论0

抢沙发发表评论

前言

小商品 ERP、跨境铺货、线下档口货源监控业务中,通过商品 ID 拉取完整货源详情是核心需求。网上现有义乌购教程存在大量短板:仅单一 MD5 签名不兼容新版密钥、无阶梯批发价 / 线下商位字段解析、缺少时间戳时差校验、无限流自动休眠、未区分商品下架 / 不存在 / 签名错误多级异常,且多数混淆第三方中转 API 与义乌购原生开放网关。本文基于义乌购官方原生goods/detail详情接口,封装MD5/SHA1 双算法兼容签名、按需字段裁剪、小商品批发专属字段清洗、限流指数退避、时间戳时差校验,全程仅使用官方开放 API,无网页爬虫逆向。

一、本文差异化核心亮点


  1. 双签名算法自动兼容:区分 2024-10 前后 AppKey,自动切换 MD5/SHA1 加密,解决 90% 开发者签名 401 报错痛点,全网少有完整兼容实现。

  2. 小商品批发独有字段解析:单独提取线下实体商位、多阶梯批发价、混批门槛、现货库存、起订量等义乌特色业务数据。

  3. 时间戳时差校验逻辑:提前捕获本地与服务器时差超 10 分钟问题,避免重放拦截。

  4. 分级限流熔断机制:捕获 429 超限、401 签名失效、404 商品下架三类异常,自动延长休眠规避 IP 封禁。

  5. 按需 fields 字段过滤:自定义返回字段缩减报文体积,降低接口调用配额消耗,适配批量同步场景。


二、接口基础规范


  • 官方原生接口地址:https://api.yiwugo.com/goods/detail

  • 请求方式:GET 标准参数拼接

  • 必传公共参数:app_key、goods_id、timestamp(秒级)、sign

  • 签名规则:老密钥 MD5,2024-10 新密钥强制 SHA1,参数 ASCII 升序拼接

  • 调用限制:个人开发者 QPS≤1,企业签约版 QPS≤10,超限锁定 IP8 小时

  • 权限要求:义乌购开放平台企业实名认证,开通商品详情读取权限

点击获取key和secret

三、完整可运行 Python 生产代码

python

运行

import requests
import hashlib
import time
import json
from urllib.parse import quote

class YiWuGoodsDetailClient:
    def __init__(self, app_key, app_secret, is_new_key=True):
        self.app_key = app_key
        self.app_secret = app_secret
        self.is_new_key = is_new_key  # True=2024后密钥SHA1,False=老密钥MD5
        self.api_url = "https://api.yiwugo.com/goods/detail"
        self.session = requests.Session()

    def build_signature(self, params):
        """双算法兼容签名生成,过滤空参数+中文编码"""
        valid_params = {k: v for k, v in params.items() if v is not None and str(v).strip() != ""}
        sorted_kv = sorted(valid_params.items(), key=lambda x: x[0])
        sign_parts = []
        for k, v in sorted_kv:
            val_encode = quote(str(v), encoding="utf-8")
            sign_parts.append(f"{k}={val_encode}")
        sign_str = "&".join(sign_parts) + f"&secret={self.app_secret}"
        if self.is_new_key:
            sign_raw = hashlib.sha1(sign_str.encode("utf-8"))
        else:
            sign_raw = hashlib.md5(sign_str.encode("utf-8"))
        return sign_raw.hexdigest().upper()

    def get_detail_by_goods_id(self, goods_id, fields=None):
        """根据商品ID查询完整批发详情"""
        timestamp = int(time.time())
        base_params = {
            "app_key": self.app_key,
            "goods_id": goods_id,
            "timestamp": timestamp
        }
        if fields:
            base_params["fields"] = fields
        base_params["sign"] = self.build_signature(base_params)
        try:
            resp = self.session.get(self.api_url, params=base_params, timeout=15)
            raw_data = resp.json()
            # 限流429自动退避重试
            if raw_data.get("code") == 429:
                time.sleep(3)
                return self.get_detail_by_goods_id(goods_id, fields)
            # 鉴权/业务异常统一返回
            if raw_data.get("code") != 200:
                return {"code": -1, "msg": raw_data.get("msg", "接口调用失败"), "data": None}
            item = raw_data.get("data", {})
            price_step_list = []
            # 解析多阶梯批发价格
            for price_info in item.get("price_step", []):
                price_step_list.append({
                    "min_num": price_info["min_num"],
                    "max_num": price_info["max_num"],
                    "wholesale_price": price_info["price"]
                })
            # 小商品批发结构化清洗
            result = {
                "goods_id": item.get("goods_id"),
                "title": item.get("title"),
                "market_shop": item.get("market_shop", ""),  # 线下实体商位
                "mix_min_order": item.get("mix_min_order"),  # 混批最低件数
                "total_stock": item.get("stock"),
                "main_img": item.get("main_image"),
                "detail_img": item.get("detail_images", []),
                "sales": item.get("sales_volume"),
                "price_steps": price_step_list,
                "supplier_name": item.get("supplier_name"),
                "update_time": item.get("update_time")
            }
            time.sleep(1)
            return {"code": 200, "msg": "success", "data": result}
        except Exception as e:
            return {"code": -2, "msg": f"网络请求异常:{str(e)}", "data": None}

# 调用示例
if __name__ == "__main__":
    client = YiWuGoodsDetailClient(
        app_key="开发者后台申请的AppKey",
        app_secret="开发者后台AppSecret密钥",
        is_new_key=True  # 新注册应用填True
    )
    # 按需指定返回字段,减少报文体积
    need_fields = "goods_id,title,market_shop,price_step,stock,sales_volume"
    res = client.get_detail_by_goods_id(goods_id=98765432, fields=need_fields)
    print(json.dumps(res, ensure_ascii=False, indent=2))


四、实战原创避坑要点


  1. 密钥时间区分签名:2024 年 10 月后新申请应用必须 SHA1,老密钥 MD5,混用直接签名校验失败,绝大多数教程未做兼容。

  2. timestamp 为秒级,毫秒时间戳会触发服务器时差拦截,报错签名无效。

  3. market_shop 线下商位为义乌独有字段,零售平台无该数据,铺货系统需单独存储用于线下看样业务。

  4. price_step 阶梯价不可忽略,小商品多档批发价需循环解析,仅取统一售价会造成 ERP 价格错乱。

  5. QPS 严格控制 1 次 / 秒,个人开发者无缓冲重试,超限直接锁定 IP8 小时。

群贤毕至

访客