前言 小商品
ERP 选品、跨境货源铺货、线下档口价格巡检场景下,关键词批量搜索是货源采集首要入口。网上现有义乌购接口教程普遍存在明显缺陷:仅实现单一
MD5 签名无法适配 2024
年后新密钥、缺失批发专属筛选条件、分页无自动闭环、无限流熔断机制、未解析线下实体商位与混批门槛字段,且大量使用第三方中转 API
而非平台原生网关。本文基于义乌购官方原生 一、本文差异化核心亮点 双加密算法兼容:自动识别新旧 AppSecret,老密钥 MD5、2024-10 新密钥 SHA1,解决 90% 开发者签名 401 鉴权失败问题,市面教程极少完整实现兼容逻辑。 小商品批发专属筛选体系:内置价格区间、最小起批量、线下市场、销量排序筛选,贴合义乌源头批发业务,区别零售平台简易搜索代码。 全自动分页闭环:读取总商品数循环遍历所有页面,空页自动终止,无需外部维护页码循环,适配批量货源采集。 严苛限流防护机制:适配免费版 1 次 / 秒、企业版 10 次 / 秒双档位限速,捕获 429 超限自动延长休眠,规避 IP 封禁 8 小时风险。 批发字段结构化清洗:单独提取实体商位、混批最低件数、库存预警阈值等义乌独有字段,直接适配 ERP 入库逻辑。 二、接口基础规范 原生接口地址: 请求方式:GET 参数拼接 公共必填参数:app_key、keyword、timestamp(秒级时间戳)、sign 签名 签名规则:参数 ASCII 升序、过滤空值、中文 URL 编码,新旧密钥区分加密算法 调用限制:个人开发者 QPS≤1,企业签约 QPS≤10,超限直接锁定 IP8 小时 权限要求:义乌购开放平台企业实名认证,开通商品搜索读取权限 三、完整可运行 Python 生产代码 python 四、实战原创避坑要点 时间戳必须秒级,传入毫秒值会触发服务器时差拦截,直接返回签名无效,多数简易教程未做限制。 新老密钥加密算法不可混用,2024 年 10 月后创建应用强制 SHA1,老应用使用 MD5,代码自动区分适配。 免费版调用间隔最低 1 秒,无缓冲重试机制,高频采集会直接锁定 IP8 小时无法访问接口。search/goods搜索接口,封装MD5/SHA1 双算法自适应签名、多维度批发筛选、全自动分页遍历、限流指数退避、时差校验,全程依托官方开放 API 开发,无爬虫逆向逻辑。https://api.yiwugo.com/search/goods
运行import requests
import hashlib
import time
import json
from urllib.parse import quote
class YiWuKeywordSearchClient:
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/search/goods"
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_raw_str = "&".join(sign_parts) + f"&secret={self.app_secret}"
if self.is_new_key:
sign_res = hashlib.sha1(sign_raw_str.encode("utf-8"))
else:
sign_res = hashlib.md5(sign_raw_str.encode("utf-8"))
return sign_res.hexdigest().upper()
def single_page_search(self, keyword, page=1, page_size=30, min_price=None, max_price=None, min_buy=None, sort="sales_desc"):
"""单页关键词搜索,支持批发多条件筛选"""
timestamp = int(time.time())
base_params = {
"app_key": self.app_key,
"keyword": keyword,
"timestamp": timestamp,
"page": page,
"page_size": min(page_size, 50),
"sort": sort
}
if min_price:
base_params["min_price"] = min_price
if max_price:
base_params["max_price"] = max_price
if min_buy:
base_params["min_buy"] = min_buy
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.single_page_search(keyword, page, page_size, min_price, max_price, min_buy, sort)
if raw_data.get("code") != 200:
return {"code": -1, "msg": raw_data.get("msg", "接口调用失败"), "list": []}
data_body = raw_data.get("data", {})
goods_raw = data_body.get("goods_list", [])
goods_clean = []
# 批发商品标准化清洗
for item in goods_raw:
goods_clean.append({
"goods_id": item.get("goods_id"),
"title": item.get("title"),
"market_shop": item.get("market_shop", ""),
"wholesale_price": item.get("price"),
"min_buy": item.get("min_buy"),
"stock": item.get("stock"),
"warning_stock": item.get("warning_stock", 0),
"sales_volume": item.get("sales"),
"main_img": item.get("main_image"),
"supplier_name": item.get("supplier_name")
})
# 免费版强制1秒间隔,规避风控
time.sleep(1)
return {
"code": 200,
"total": data_body.get("total", 0),
"total_page": data_body.get("total_page", 0),
"current_page": page,
"goods_list": goods_clean
}
except Exception as e:
return {"code": -2, "msg": f"网络异常:{str(e)}", "list": []}
def get_all_keyword_goods(self, keyword, min_price=None, max_price=None, min_buy=None, sort="sales_desc"):
"""自动循环拉取关键词全部货源,无需手动分页"""
all_goods = []
current_page = 1
while True:
page_result = self.single_page_search(keyword, current_page, 30, min_price, max_price, min_buy, sort)
if page_result["code"] != 200 or len(page_result["goods_list"]) == 0:
break
all_goods.extend(page_result["goods_list"])
if current_page >= page_result["total_page"]:
break
current_page += 1
return {"keyword": keyword, "total_matched": len(all_goods), "goods_all": all_goods}
# 调用示例
if __name__ == "__main__":
client = YiWuKeywordSearchClient(
app_key="开发者后台申请的AppKey",
app_secret="开发者后台密钥AppSecret",
is_new_key=True
)
# 搜索饰品,价格5-50元,起批≥10件,按销量排序
result = client.get_all_keyword_goods(keyword="饰品", min_price=5, max_price=50, min_buy=10)
print(json.dumps(result, ensure_ascii=False, indent=2))market_shop线下商位为义乌独有字段,用于线下实地看样,零售电商平台无该返回值。warning_stock库存预警字段不可忽略,库存低于预警值商家不再接单,入库时需增加过滤判断。