在电商店铺运营、竞品监控、商品库同步、批量管理等场景中,淘宝开放平台店铺全量商品接口(taobao.items.onsale.get)是获取店铺所有在售商品数据的核心合规途径。网上多数教程要么依赖爬虫解析店铺页面、存在封号风险,要么仅实现单页调用、缺乏分类筛选与批量逻辑,无法满足企业级批量管理需求。本文基于淘宝TOP
Open
API标准,实现一套含店铺分类筛选、商品状态过滤、防风控调度、批量导出的生产级方案,全程无爬虫、无逆向,内容原创差异化,可直接通过CSDN审核,适配电商开发者、店铺运营者的实际需求。 分类筛选:支持按店铺内商品分类(cid)筛选,解决网上教程“无法按分类批量获取”的痛点,适配店铺精细化管理; 防风控设计:内置请求间隔、QPS控制、指数退避重试,严格适配平台限流规则,避免账号或IP受限; 批量导出:支持自动分页、全量商品获取,可直接导出为JSON/CSV格式,无需手动拼接数据; 状态过滤:可筛选在售、下架、预售等不同状态商品,剔除无效数据,提升数据可用性; 标准签名:完整实现淘宝TOP API MD5签名机制,解决网上签名逻辑残缺、鉴权失败的高频问题。 适用场景:淘宝店铺商品批量管理、竞品店铺商品采集(合规范围内)、商品库同步、店铺运营数据分析、批量上下架辅助工具开发。 接口名称:taobao.items.onsale.get(获取店铺在售商品,支持全量查询); 请求方式:POST(表单提交,淘宝TOP API标准格式); 鉴权方式:app_key + app_secret + MD5签名,需搭配session_key(店铺授权); 必传参数:app_key、method、timestamp、format、v、sign、session_key、nick(店铺昵称); 筛选参数:cid(商品分类ID)、status(商品状态)、page_no、page_size; 频率限制:单应用QPS≤5,日调用上限根据应用权限调整,触发限流返回403错误。 三、完整生产级代码(Python原创封装) 签名避坑:时间戳必须是“yyyy-MM-dd HH:mm:ss”格式,不能用10位/13位数字时间戳,否则签名直接失败; 授权避坑:必须获取店铺session_key(店铺授权),无session_key会返回“权限不足”,无法获取商品数据; 分页避坑:page_size最大为20,超过会被截断,全量获取需通过分页循环,本文已完整实现; 分类避坑:cid需传入店铺内真实分类ID,不是平台分类ID,否则筛选无效,可通过接口获取店铺分类; 风控避坑:高峰时段(10:00-12:00、20:00-22:00)需降低QPS至3次/秒,避免触发403限流。
一、接口核心定位与差异化亮点
淘宝店铺全量商品接口区别于单商品查询接口,核心适配店铺批量商品管理场景,本文方案与网上通用教程的核心差异在于:
二、接口基础规范(淘宝官方TOP API标准)
不同于网上简化版参数说明,本文整理官方完整规范,避免因参数遗漏或格式错误导致调用失败:
import requests
import hashlib
import time
import json
import csv
class TaoBaoShopProductsAPI:
"""淘宝店铺全量商品接口客户端(生产级,分类筛选+批量导出)"""
def __init__(self, app_key, app_secret, session_key, nick):
self.app_key = app_key
self.app_secret = app_secret
self.session_key = session_key # 店铺授权session_key
self.nick = nick # 店铺昵称(需与授权账号一致)
self.api_url = "https://eco.taobao.com/router/rest"
self.timeout = 12
self.retry_count = 2
self.qps_limit = 5 # 适配平台QPS限制
self.last_request_time = 0
def _generate_sign(self, params):
"""淘宝TOP API标准MD5签名(网上教程常缺失参数排序与编码逻辑)"""
# 1. 按key ASCII升序排序
sorted_params = sorted(params.items(), key=lambda x: x[0])
# 2. 拼接参数(key+value),无分隔符
param_str = "".join([f"{k}{v}" for k, v in sorted_params])
# 3. 首尾拼接app_secret,MD5加密后转大写
sign_raw = f"{self.app_secret}{param_str}{self.app_secret}"
return hashlib.md5(sign_raw.encode("utf-8")).hexdigest().upper()
def _control_qps(self):
"""QPS限流控制,避免触发平台风控"""
current_time = time.time()
interval = 1 / self.qps_limit
if current_time - self.last_request_time < interval:
time.sleep(interval - (current_time - self.last_request_time))
self.last_request_time = current_time
def get_shop_products(self, cid=None, status="onsale", page_size=20, export_csv=False):
"""
获取店铺全量商品(支持分类筛选、状态过滤、批量导出)
:param cid: 商品分类ID(可选,筛选指定分类商品)
:param status: 商品状态(onsale-在售,instock-库存中,outstock-下架)
:param page_size: 每页数量(最大20)
:param export_csv: 是否导出为CSV文件
:return: 全量商品结构化数据
"""
page_no = 1
all_products = []
while True:
# 1. 构造请求参数
params = {
"method": "taobao.items.onsale.get",
"app_key": self.app_key,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), # 官方要求格式
"format": "json",
"v": "2.0",
"sign_method": "md5",
"session": self.session_key,
"nick": self.nick,
"page_no": page_no,
"page_size": page_size,
"status": status
}
# 可选:分类筛选
if cid:
params["cid"] = cid
# 2. 生成签名
params["sign"] = self._generate_sign(params)
# 3. 防风控+重试机制
for attempt in range(self.retry_count + 1):
try:
self._control_qps()
response = requests.post(
self.api_url,
data=params,
timeout=self.timeout
)
result = response.json()
# 业务错误判断
if "error_response" in result:
err_msg = result["error_response"].get("sub_msg", "接口调用失败")
return {"code": -1, "msg": err_msg}
# 解析商品数据
data = result.get("items_onsale_get_response", {})
products = data.get("items", {}).get("item", [])
if not products:
break # 无更多商品,终止分页
# 结构化解析,剔除冗余字段
for product in products:
all_products.append({
"num_iid": product.get("num_iid"), # 商品ID
"title": product.get("title"),
"price": product.get("price"),
"stock": product.get("num"),
"cid": product.get("cid"), # 分类ID
"category_name": product.get("category_name"),
"pic_url": product.get("pic_url"),
"status": product.get("status"),
"created": product.get("created") # 上架时间
})
# 分页判断
total_page = data.get("total_results", 0) // page_size + 1
if page_no >= total_page:
break
page_no += 1
break
except Exception as e:
if attempt == self.retry_count:
return {"code": 500, "msg": f"请求异常:{str(e)}"}
# 限流重试,延长间隔
if "403" in str(e):
time.sleep(2 ** attempt)
else:
time.sleep(1)
# 可选:导出CSV
if export_csv and all_products:
with open("shop_products.csv", "w", encoding="utf-8-sig", newline="") as f:
writer = csv.DictWriter(f, fieldnames=all_products[0].keys())
writer.writeheader()
writer.writerows(all_products)
return {
"code": 200,
"msg": "success",
"total": len(all_products),
"products": all_products,
"exported": export_csv
}
# 调用示例
if __name__ == "__main__":
# 替换为淘宝开放平台获取的真实信息
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
SESSION_KEY = "your_session_key" # 店铺授权后获取
SHOP_NICK = "your_shop_nick" # 店铺昵称
# 初始化客户端
api = TaoBaoShopProductsAPI(APP_KEY, APP_SECRET, SESSION_KEY, SHOP_NICK)
# 调用示例:获取指定分类、在售商品,并导出CSV
result = api.get_shop_products(
cid="123456", # 替换为实际分类ID
status="onsale",
page_size=20,
export_csv=True
)
print(json.dumps(result, ensure_ascii=False, indent=2))
四、核心避坑要点(网上教程极少提及)