前言 在跨境选品、竞品监控、价格分析、爆款挖掘场景中,亚马逊商品搜索是核心入口。网上多数教程仍停留在旧版 PAAPI 或简单 HTTP 请求,普遍存在认证逻辑过时、多站点适配差、签名错误、分页断裂、无令牌刷新、缺少商品画像结构化等问题,无法直接用于生产环境。 本文基于亚马逊最新SP-API(Selling Partner API) 的 一、本文核心差异化亮点 最新 SP-API 认证:LWA 令牌自动刷新,解决旧 PAAPI 鉴权失效问题 标准 SigV4 签名:严格遵循 AWS 签名规范,适配亚马逊全球站点 多站点一键适配:支持北美 / 欧洲 / 日本等 10 + 站点,自动匹配市场 ID 智能分页容错:自动处理空页、重复页、限流重试,适配大数据量拉取 商品画像全解析:结构化提取价格区间、评分分布、类目路径、高清图 二、接口基础规范 接口名称: 请求地址: 认证方式:LWA(Login with Amazon) 令牌 + SigV4 签名Amazon 时间戳:UTC ISO8601 格式 频率限制:QPS ≤ 5,日调用上限随开发者等级 权限要求:SP-API 目录商品读取权限 三、完整 Python 生产级代码 python 四、实战避坑干货(原创) 必须用 SP-API 而非 PAAPI:旧 PAAPI 已停止维护,新应用仅支持 SP-API LWA 令牌有效期短:必须提前 60 秒刷新,避免调用时报 SigV4 签名必须包含 Host:缺少 Host 会导致签名验证失败,90% 教程遗漏 多站点 region 对应正确:北美→na、欧洲→eu、日本→jp,否则路由错误 includedData 按需指定:仅拉取需要的字段(如 images/ratings),减少响应体积catalog/items接口,实现LWA 令牌自动刷新、SigV4 标准签名、多站点一键切换、分页容错、商品画像(标题 / 价格 / 评分 / 图片 / 类目)结构化输出,全程合规无爬虫,原创差异化强。catalog/2022-04-01/items(SP-API 最新版)Amazonhttps://sellingpartnerapi-{region}.amazon.com
运行import requests
import hmac
import hashlib
import time
import json
from datetime import datetime, timedelta
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
class AmazonSPSearchAPI:
"""亚马逊SP-API商品搜索接口(生产级·合规封装)"""
def __init__(self, client_id, client_secret, refresh_token, aws_access_key, aws_secret_key):
self.client_id = client_id
self.client_secret = client_secret
self.refresh_token = refresh_token
self.aws_access_key = aws_access_key
self.aws_secret_key = aws_secret_key
self.access_token = None
self.token_expire = None
# 多站点市场ID映射
self.marketplaces = {
"us": ("na", "ATVPDKIKX0DER"),
"uk": ("eu", "A1F83G8C2ARO7P"),
"de": ("eu", "A1PA6795UKMFR9"),
"jp": ("jp", "A1VC38T7YXB528")
}
self._refresh_access_token()
def _refresh_access_token(self):
"""LWA令牌自动刷新(核心:解决过期问题)"""
url = "https://api.amazon.com/auth/o2/token"
data = {
"grant_type": "refresh_token",
"refresh_token": self.refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret
}
try:
resp = requests.post(url, data=data, timeout=10)
token_data = resp.json()
self.access_token = token_data.get("access_token")
expires_in = token_data.get("expires_in", 3600)
self.token_expire = datetime.utcnow() + timedelta(seconds=expires_in - 60)
except Exception as e:
raise Exception(f"令牌刷新失败:{str(e)}")
def _sigv4_sign(self, url, method, headers, payload):
"""SigV4标准签名(亚马逊官方要求)"""
service = "sellingpartnerapi"
region = url.split("-")[-1].split(".")[0]
request = AWSRequest(method=method, url=url, headers=headers, data=payload)
SigV4Auth(
credentials={"access_key": self.aws_access_key, "secret_key": self.aws_secret_key},
service_name=service,
region_name=region
).add_auth(request)
return dict(request.headers)
def search_items(self, keywords, site="us", page=1, page_size=20):
"""
商品搜索:支持多站点、分页、结构化解析
:param keywords: 搜索关键词
:param site: 站点(us/uk/de/jp)
:param page: 页码
:param page_size: 每页数量(最大50)
:return: 结构化商品数据
"""
# 令牌过期检查
if datetime.utcnow() >= self.token_expire:
self._refresh_access_token()
if site not in self.marketplaces:
return {"code": -1, "msg": "不支持的站点"}
region, marketplace_id = self.marketplaces[site]
url = f"https://sellingpartnerapi-{region}.amazon.com/catalog/2022-04-01/items"
params = {
"keywords": keywords,
"marketplaceIds": marketplace_id,
"includedData": ["summaries", "images", "offers", "ratings"],
"pageSize": page_size,
"page": page
}
payload = json.dumps(params)
headers = {
"x-amz-access-token": self.access_token,
"Content-Type": "application/json",
"Host": f"sellingpartnerapi-{region}.amazon.com"
}
signed_headers = self._sigv4_sign(url, "POST", headers, payload)
try:
resp = requests.post(url, headers=signed_headers, data=payload, timeout=15)
result = resp.json()
if resp.status_code != 200:
return {"code": -2, "msg": f"请求失败:{result.get('message')}", "error": result}
items = result.get("items", [])
total = result.get("totalCount", 0)
product_list = []
# 结构化商品画像解析
for item in items:
summary = item.get("summaries", [{}])[0]
image = item.get("images", [{}])[0].get("images", [{}])[0]
offer = item.get("offers", [{}])[0].get("price", {})
rating = item.get("ratings", {}).get("averageRating", 0)
product_list.append({
"asin": item.get("asin"),
"title": summary.get("title", ""),
"brand": summary.get("brand", {}).get("name", ""),
"price": offer.get("amount", 0),
"currency": offer.get("currencyCode", "USD"),
"rating": rating,
"review_count": item.get("ratings", {}).get("reviewCount", 0),
"main_image": image.get("link", ""),
"category": summary.get("categories", [{}])[0].get("name", "")
})
time.sleep(0.5)
return {
"code": 200,
"msg": "success",
"data": {
"keywords": keywords,
"site": site,
"total_count": total,
"page": page,
"product_list": product_list
}
}
except Exception as e:
return {"code": 500, "msg": f"异常:{str(e)}"}
# ———— 调用示例 ————
if __name__ == "__main__":
API = AmazonSPSearchAPI(
client_id="你的LWA客户端ID",
client_secret="你的LWA客户端密钥",
refresh_token="你的LWA刷新令牌",
aws_access_key="你的AWS访问密钥",
aws_secret_key="你的AWS密钥"
)
# 搜索美国站蓝牙耳机
res = API.search_items("wireless earbuds", site="us", page=1)
print(json.dumps(res, ensure_ascii=False, indent=2))invalid tokenAmazon