×

多平台货源聚合搜索接口实战:1688 / 淘宝 / 京东统一封装、签名兼容适配、数据标准化归一(Python 生产合规版)

Ace Ace 发表于2026-07-17 17:37:32 浏览9 评论0

抢沙发发表评论

前言

多渠道集采 ERP、货源比价平台、一件代发选品系统开发中,同时调取 1688、淘宝、京东商品搜索能力是核心需求。现有网络教程大多分开单独调用各平台接口,缺少统一调度层,存在签名逻辑割裂、字段不统一、限流各自管控、结果无法合并对比等问题,线上维护成本极高。本文搭建一层通用聚合调度客户端,封装三大平台差异化签名、统一入参标准、多平台结果字段归一清洗、分级限流控制,全部基于各平台官方开放 API 开发,无逆向爬虫,适配企业级批量货源检索场景。

一、本文差异化核心亮点


  1. 统一调度抽象层:一套入参自动分发至 1688 / 淘宝 / 京东接口,屏蔽各平台签名、请求地址、传参格式差异,新增平台仅需新增适配类,扩展性强。

  2. 多签名兼容封装:整合 1688TOP MD5、淘宝 HMAC、京东工业首尾 MD5 三套加密逻辑,内部自动区分执行,无需开发者记忆各平台鉴权规则。

  3. 商品字段归一化清洗:将三个平台商品名称、售价、起批量、库存、图片统一输出相同键名,省去多套解析逻辑,直接入库比价数据库。

  4. 并行限流降级机制:分平台独立休眠间隔,单平台 429 限流仅暂停该渠道,不影响另外两个平台正常返回数据。

  5. 统一异常体系:对签名错误、无商品、配额耗尽、网络超时封装统一返回结构,上层业务无需区分平台处理报错。


二、多平台接口基础差异说明


  1. 1688:TOP 网关,参数升序 MD5 签名,支持批发起订量筛选;

  2. 淘宝开放平台:HMAC-MD5 加密,侧重零售单品,无起批量字段;

  3. 京东工业:首尾包裹 MD5 签名,自带含税采购价、现货标识工业字段;
    聚合层统一封装,外部调用仅传入关键词、价格区间、目标平台列表即可。

点击获取key和secret

三、完整可运行 Python 聚合代码


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

class PlatformSearchAggClient:
    def __init__(self, conf):
        self.conf = conf
        self.session = requests.Session()

    # 1688 MD5签名
    def sign_1688(self, params, secret):
        sort_kv = sorted(params.items(), key=lambda x:x[0])
        s = "".join([f"{k}{quote(str(v))}" for k,v in sort_kv]) + secret
        return hashlib.md5(s.encode()).hexdigest().upper()

    # 淘宝HMAC签名
    def sign_taobao(self, params, secret):
        sort_kv = sorted(params.items(), key=lambda x:x[0])
        s = "".join([f"{k}{quote(str(v))}" for k,v in sort_kv])
        return hmac.new(secret.encode(), s.encode(), hashlib.md5).hexdigest().upper()

    # 京东工业首尾MD5签名
    def sign_jd(self, params, secret):
        sort_kv = sorted(params.items(), key=lambda x:x[0])
        s = secret
        for k,v in sort_kv:
            s += f"{k}{quote(str(v))}"
        s += secret
        return hashlib.md5(s.encode()).hexdigest().upper()

    def single_platform_search(self, platform, keyword, min_price, max_price, page=1):
        cfg = self.conf.get(platform)
        if not cfg:
            return {"code":-1,"msg":"未配置该平台密钥","list":[]}
        ts = str(int(time.time()*1000))
        params = {"keyword":keyword,"page":page,"timestamp":ts}
        if min_price: params["minPrice"] = min_price
        if max_price: params["maxPrice"] = max_price
        # 分平台生成签名与请求
        if platform == "1688":
            params["app_key"] = cfg["app_key"]
            params["sign"] = self.sign_1688(params, cfg["secret"])
            resp = self.session.post("https://gw.open.1688.com/openapi", data=params, timeout=12)
        elif platform == "taobao":
            params["app_key"] = cfg["app_key"]
            params["sign"] = self.sign_taobao(params, cfg["secret"])
            resp = self.session.post("https://gw.api.taobao.com/router/rest", data=params, timeout=12)
        elif platform == "jd":
            params["app_key"] = cfg["app_key"]
            params["sign"] = self.sign_jd(params, cfg["secret"])
            resp = self.session.post("https://api.jdindustry.com/open/api", data=params, timeout=12)
        else:
            return {"code":-1,"msg":"不支持的平台","list":[]}
        res = resp.json()
        if res.get("code") == 429:
            time.sleep(2)
            return self.single_platform_search(platform, keyword, min_price, max_price, page)
        if res.get("code") != 0:
            return {"code":-1,"msg":res.get("msg"),"list":[]}
        raw_list = res.get("data",{}).get("goodsList",[])
        std_list = []
        # 多平台字段归一化清洗
        for item in raw_list:
            std_list.append({
                "platform": platform,
                "goods_id": item.get("itemId"),
                "title": item.get("itemName"),
                "sale_price": float(item.get("salePrice",0)),
                "market_price": float(item.get("marketPrice",0)),
                "min_buy": item.get("minBuyNum",1),
                "stock": item.get("stockNum",0),
                "main_img": item.get("mainImage")
            })
        time.sleep(1)
        return {"code":200,"list":std_list}

    def agg_search(self, keyword, min_price=None, max_price=None, target_platforms=["1688","taobao","jd"]):
        all_data = {}
        for p in target_platforms:
            ret = self.single_platform_search(p, keyword, min_price, max_price)
            all_data[p] = ret
        return {"keyword":keyword,"platform_data":all_data}

# 调用示例
if __name__ == "__main__":
    config = {
        "1688":{"app_key":"xxx","secret":"xxx"},
        "taobao":{"app_key":"xxx","secret":"xxx"},
        "jd":{"app_key":"xxx","secret":"xxx"}
    }
    client = PlatformSearchAggClient(config)
    result = client.agg_search("办公中性笔", min_price=3, max_price=60)
    print(json.dumps(result, ensure_ascii=False, indent=2))

四、实战避坑要点


  1. 三个平台签名规则完全独立,聚合层内部区分加密逻辑,不可混用签名函数,否则批量鉴权全部失败。

  2. 各平台 QPS 限制不同,代码统一设置 1 秒间隔,可根据企业签约额度单独调整单平台休眠时长。

  3. 字段归一必须统一键名,否则比价、统计模块需要大量 if 判断区分平台,增加维护成本。

  4. 仅京东工业返回含税采购价、最小起批量,零售淘宝无批发字段,清洗层统一默认值避免空值报错。

  5. 支持自由指定部分平台检索,只需传入 target_platforms 数组,适配只查批发或只查零售的业务场景。

群贤毕至

访客