×

深度分析虾皮API接口,用Python脚本实现

万邦科技Lex 万邦科技Lex 发表于2025-08-20 09:05:14 浏览23 评论0

抢沙发发表评论

        免费测试虾皮API接口

虾皮(Shopee)作为东南亚及台湾地区领先的电商平台,其开放平台提供了完善的 API 接口,支持商品管理、订单处理、价格查询等功能。以下基于官方 API 进行深度分析,并实现 Python 调用方案,重点获取商品价格及优惠信息(到手价)。

### 一、虾皮 API 核心特性分析

#### 1. 接口体系与功能域

虾皮开放平台 API 按功能分为以下核心域:

-   **商品管理**:商品列表、商品详情、价格更新等;

-   **促销管理**:折扣活动、优惠券;

-   **订单管理**:订单列表、订单详情、物流信息等;

-   **店铺管理**:店铺信息、运营数据统计等。

  获取商品 “到手价”(含折扣、优惠券后的最终价格)主要依赖**商品详情接口**和**促销接口**的组合调用。

#### 2. 认证与请求规范

-   **认证机制**:采用`partner_id + shopid + timestamp + signature`认证:

    -   `partner_id`:开发者 ID(从开放平台获取);

    -   `shopid`:店铺 ID(需绑定店铺授权);

    -   `timestamp`:请求时间戳(秒级,与服务器时间误差≤15 分钟);

    -   `signature`:签名(通过`partner_key`对请求参数加密生成)。

-   **请求格式**:支持 GET/POST,参数以 JSON 或表单形式传递,响应为 JSON 格式;

-   **区域差异**:不同站点(如新加坡、台湾、马来西亚)有独立 API 网关,例如:

    -   新加坡站:

    -   台湾站:

#### 3. 核心接口参数与响应(以商品详情为例)

-   **接口名称**:`/api/v2/item/get`(获取商品详情)

-   **请求参数**:

    -   `item_id`:商品 ID(必填);

    -   `partner_id`、`shopid`、`timestamp`、`signature`(认证参数)。

-   **响应核心字段**:

    -   `price`:商品原价(单位:分,对应站点货币,如新台币、马来西亚令吉);

    -   `discount_price`:折扣价(如有活动);

    -   `promotion_id`:关联的促销活动 ID(用于查询详细优惠)。

### 二、Python 脚本实现:虾皮 API 调用框架

以下实现基于官方 API,支持商品详情查询及到手价计算,包含签名生成、接口调用及响应解析。

import requests

import json

import time

import hashlib

import logging

from typing import Dict, Optional, Tuple

from requests.exceptions import RequestException


# 配置日志

logging.basicConfig(

    level=logging.INFO,

    format="%(asctime)s - %(levelname)s - %(message)s"

)


class ShopeeAPI:

    def __init__(self, partner_id: int, partner_key: str, shopid: int, region: str = "sg"):

        """

        初始化虾皮API客户端

        :param partner_id: 开发者partner_id(从开放平台获取)

        :param partner_key: 开发者partner_key(密钥)

        :param shopid: 店铺ID(需提前授权)

        :param region: 站点区域(sg:新加坡, tw:台湾, my:马来西亚等)

        """

        self.partner_id = partner_id

        self.partner_key = partner_key

        self.shopid = shopid

        self.region = region

        # 不同区域的API网关

        self.gateways = {

            "sg": "https://partner.shopeemobile.com",

            "tw": "https://partner.shopee.tw",

            "my": "https://partner.shopee.com.my",

            "th": "https://partner.shopee.co.th"

        }

        self.base_url = self.gateways.get(region, self.gateways["sg"])

        self.session = requests.Session()


    def _generate_signature(self, params: Dict) -> str:

        """

        生成签名(虾皮API签名规则)

        规则:将所有参数按key升序排序,拼接为key=value&key=value格式,

              末尾拼接partner_key,通过SHA256加密

        """

        # 1. 按参数名ASCII升序排序

        sorted_params = sorted(params.items(), key=lambda x: x[0])

        # 2. 拼接为key=value&key=value格式

        sign_str = "&".join([f"{k}={v}" for k, v in sorted_params])

        # 3. 拼接partner_key并SHA256加密

        sign_str += self.partner_key

        return hashlib.sha256(sign_str.encode("utf-8")).hexdigest()


    def _get_timestamp(self) -> int:

        """生成秒级时间戳"""

        return int(time.time())


    def call_api(self, path: str, params: Dict) -> Optional[Dict]:

        """

        通用API调用方法

        :param path: 接口路径(如/api/v2/item/get)

        :param params: 业务参数

        :return: 接口响应数据

        """

        # 1. 构建基础参数(认证信息)

        base_params = {

            "partner_id": self.partner_id,

            "shopid": self.shopid,

            "timestamp": self._get_timestamp()

        }

        # 2. 合并业务参数

        all_params = {**base_params,** params}

        # 3. 生成签名

        signature = self._generate_signature(all_params)

        all_params["signature"] = signature


        # 4. 发送请求(虾皮API以GET为主,部分接口支持POST)

        url = f"{self.base_url}{path}"

        try:

            response = self.session.get(

                url,

                params=all_params,

                timeout=15

            )

            response.raise_for_status()

            result = response.json()


            # 5. 处理错误响应(虾皮API错误码在error字段)

            if "error" in result and result["error"] != 0:

                logging.error(f"API错误:{result.get('message')}(错误码:{result['error']})")

                return None


            logging.info(f"API调用成功:{path},响应长度:{len(json.dumps(result))}")

            return result


        except RequestException as e:

            logging.error(f"请求异常:{str(e)},接口:{path}")

            return None

        except json.JSONDecodeError:

            logging.error(f"响应格式错误:{response.text[:200]}...,接口:{path}")

            return None


    def get_item_detail(self, item_id: int) -> Optional[Dict]:

        """

        获取商品详情(含价格、折扣信息)

        :param item_id: 商品ID

        :return: 商品详情字典

        """

        path = "/api/v2/item/get"

        params = {"item_id": item_id}

        result = self.call_api(path, params)

        if not result or "item" not in result:

            return None


        item = result["item"]

        # 价格单位转换(分→元)

        price = item.get("price") / 100 if item.get("price") else None

        discount_price = item.get("discount_price") / 100 if item.get("discount_price") else None


        # 提取促销ID(用于查询详细优惠规则)

        promotion_id = item.get("promotion_id")


        return {

            "item_id": item_id,

            "name": item.get("name"),

            "price": price,  # 原价

            "discount_price": discount_price,  # 折扣价(可能为到手价)

            "currency": item.get("currency"),  # 货币单位(如TWD、SGD)

            "stock": item.get("stock"),  # 库存

            "promotion_id": promotion_id,

            "image_url": item.get("image")  # 主图URL

        }


    def get_promotion_detail(self, promotion_id: int) -> Optional[Dict]:

        """

        获取促销活动详情(用于计算最终到手价)

        :param promotion_id: 促销活动ID(从商品详情获取)

        :return: 促销详情

        """

        if not promotion_id:

            return None


        path = "/api/v2/promotion/discount/get"

        params = {"promotion_id": promotion_id}

        result = self.call_api(path, params)

        if not result or "promotion" not in result:

            return None


        promotion = result["promotion"]

        return {

            "promotion_id": promotion_id,

            "type": promotion.get("promotion_type"),  # 促销类型(如限时折扣)

            "discount_rate": promotion.get("discount_rate"),  # 折扣率(百分比)

            "start_time": promotion.get("start_time"),

            "end_time": promotion.get("end_time")

        }


    def calculate_final_price(self, item_id: int) -> Tuple[Optional[float], Optional[Dict]]:

        """

        计算商品到手价(综合原价、折扣、促销)

        :param item_id: 商品ID

        :return: 到手价 + 价格详情

        """

        item_detail = self.get_item_detail(item_id)

        if not item_detail:

            return None, None

        # 到手价优先取折扣价,若无则取原价

        final_price = item_detail["discount_price"] or item_detail["price"]

        promotion_detail = None


        # 若有促销活动,进一步补充信息

        if item_detail["promotion_id"]:

            promotion_detail = self.get_promotion_detail(item_detail["promotion_id"])

            # 部分促销可能叠加折扣(如额外9折)

            if promotion_detail and promotion_detail.get("discount_rate"):

                discount_rate = promotion_detail["discount_rate"] / 100  # 转换为小数

                final_price = final_price * discount_rate

        price_info = {

            "original_price": item_detail["price"],

            "discount_price": item_detail["discount_price"],

            "final_price": round(final_price, 2),

            "currency": item_detail["currency"],

            "promotion": promotion_detail

        }

        return price_info["final_price"], price_info

# 示例调用

if __name__ == "__main__":

    # 替换为实际参数(从虾皮开放平台获取)

    PARTNER_ID = 123456  # 你的partner_id

    PARTNER_KEY = "your_partner_key"  # 你的partner_key

    SHOPID = 789012  # 授权的店铺ID

    REGION = "tw"  # 台湾站


    # 初始化客户端

    shopee_api = ShopeeAPI(

        partner_id=PARTNER_ID,

        partner_key=PARTNER_KEY,

        shopid=SHOPID,

        region=REGION

    )


    # 示例商品ID(需替换为目标店铺的实际商品ID)

    ITEM_ID = 123456789


    # 获取商品到手价

    final_price, price_info = shopee_api.calculate_final_price(ITEM_ID)


    if price_info:

        print(f"商品名称:{shopee_api.get_item_detail(ITEM_ID)['name']}")

        print(f"原价:{price_info['original_price']} {price_info['currency']}")

        print(f"折扣价:{price_info['discount_price'] or '无'} {price_info['currency']}")

        print(f"到手价:{price_info['final_price']} {price_info['currency']}")

        

        if price_info["promotion"]:

            print(f"促销活动:{price_info['promotion']['type']}({price_info['promotion']['discount_rate']}折)")

            print(f"活动时间:{price_info['promotion']['start_time']} 至 {price_info['promotion']['end_time']}")

### 三、关键技术点解析

#### 1. 签名生成逻辑

虾皮 API 签名是核心安全机制,实现步骤:

1.  收集所有请求参数(含`partner_id`、`shopid`、`timestamp`及业务参数);

1.  按参数名 ASCII 码升序排序;

1.  拼接为`key=value&key=value`格式;

1.  末尾拼接`partner_key`,通过 SHA256 加密生成签名;

1.  将签名作为`signature`参数加入请求。

#### 2. 价格计算逻辑

“到手价” 计算需综合以下因素:

-   **原价(`price`)** :商品基础价格,单位为 “分”(需除以 100 转换为元);

-   **折扣价(`discount_price`)** :商品参与基础折扣后的价格(如店铺日常折扣);

-   **促销活动**:通过`promotion_id`查询详细促销(如限时折扣、满减),可能存在叠加折扣(如额外 9 折)

最终到手价 = 折扣价(或原价) × 促销折扣率(如有)。

#### 3. 区域与货币处理

-   虾皮各站点使用不同货币(如台湾站为新台币 TWD,新加坡站为新加坡元 SGD),响应中`currency`字段标识货币类型;

-   API 网关需匹配对应区域(通过`region`参数指定),否则会导致请求失败。

### 四、使用注意事项

1.  **权限申请**:需在注册开发者账号,创建应用并绑定店铺(获取`shopid`);

1.  **接口限制**:

    -   单 IP QPS 限制为 100(不同接口可能不同);

    -   部分接口(如促销详情)需额外申请权限;

1.  **时间同步**:`timestamp`需与虾皮服务器时间误差≤15 分钟,否则签名验证失败;

1.  **错误处理**:常见错误码如`1001`(签名错误)、`1002`(参数缺失),需根据响应信息调试。

该实现基于官方 API,稳定性和合规性有保障,适用于跨境电商数据分析、店铺运营工具等场景。通过扩展可支持批量商品查询、价格监控等功能,实际使用需参考调整参数和接口。


群贤毕至

访客