×

亚马逊商品搜索接口实战:SP-API 认证 + 多站点分页 + 商品画像结构化(Python 合规版)

Ace Ace 发表于2026-06-10 13:32:44 浏览14 评论0

抢沙发发表评论

前言

在跨境选品、竞品监控、价格分析、爆款挖掘场景中,亚马逊商品搜索是核心入口。网上多数教程仍停留在旧版 PAAPI 或简单 HTTP 请求,普遍存在认证逻辑过时、多站点适配差、签名错误、分页断裂、无令牌刷新、缺少商品画像结构化等问题,无法直接用于生产环境。

本文基于亚马逊最新SP-API(Selling Partner API)catalog/items接口,实现LWA 令牌自动刷新、SigV4 标准签名、多站点一键切换、分页容错、商品画像(标题 / 价格 / 评分 / 图片 / 类目)结构化输出,全程合规无爬虫,原创差异化强。

一、本文核心差异化亮点


  1. 最新 SP-API 认证:LWA 令牌自动刷新,解决旧 PAAPI 鉴权失效问题

  2. 标准 SigV4 签名:严格遵循 AWS 签名规范,适配亚马逊全球站点

  3. 多站点一键适配:支持北美 / 欧洲 / 日本等 10 + 站点,自动匹配市场 ID

  4. 智能分页容错:自动处理空页、重复页、限流重试,适配大数据量拉取

  5. 商品画像全解析:结构化提取价格区间、评分分布、类目路径、高清图


二、接口基础规范

  • 接口名称:catalog/2022-04-01/items(SP-API 最新版)Amazon

  • 请求地址:https://sellingpartnerapi-{region}.amazon.com

  • 认证方式:LWA(Login with Amazon) 令牌 + SigV4 签名Amazon

  • 时间戳:UTC ISO8601 格式

  • 频率限制:QPS ≤ 5,日调用上限随开发者等级

  • 权限要求:SP-API 目录商品读取权限

点击获取key和secret

三、完整 Python 生产级代码

python

运行

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))

四、实战避坑干货(原创)

  1. 必须用 SP-API 而非 PAAPI:旧 PAAPI 已停止维护,新应用仅支持 SP-API

  2. LWA 令牌有效期短:必须提前 60 秒刷新,避免调用时报invalid tokenAmazon

  3. SigV4 签名必须包含 Host:缺少 Host 会导致签名验证失败,90% 教程遗漏

  4. 多站点 region 对应正确:北美→na、欧洲→eu、日本→jp,否则路由错误

  5. includedData 按需指定:仅拉取需要的字段(如 images/ratings),减少响应体积


群贤毕至

访客