×

拼多多 item_search_best 接口深度分析及 Python 实现

万邦科技Lex 万邦科技Lex 发表于2025-09-14 15:36:24 浏览224 评论0

抢沙发发表评论

      注册账号免费测试拼多多API数据接口

拼多多开放平台(Pinduoduo Open Platform)的 item_search_best 接口是核心商品搜索接口之一,专注于返回 “最优匹配” 的商品结果,其排序逻辑结合了商品销量、价格、评分、商家资质等多维度权重,适用于精准商品检索、竞品分析、市场行情监控等场景。本文将从接口特性、参数解析、返回数据结构、调用规范及 Python 实现等方面进行深度分析。

一、item_search_best 接口核心特性

1. 接口定位与核心价值

item_search_best 不同于基础搜索接口(如 item_search),其核心特点是 **“权重优先排序”**,而非简单的关键词匹配。拼多多官方对该接口的定位是:


  • 优先返回 “高性价比” 商品(销量高、价格合理、评分优)

  • 兼顾商家资质(如旗舰店、专营店优先级高于普通店铺)

  • 支持多维度筛选(价格区间、销量、发货地等)

  • 适用于 “找爆款”“竞品对标”“市场调研” 等场景

2. 接口权限与调用限制

使用 item_search_best 接口需满足拼多多开放平台的权限要求,且存在严格的调用限制,具体如下:


限制类型具体规则说明
权限要求需申请 “商品搜索” 类目权限个人开发者需完成实名认证,企业开发者需完成企业认证
调用频率默认 10 次 / 分钟(QPS ≈ 0.17)企业开发者可申请提升至 30 次 / 分钟,需提交业务场景说明
数据返回单次最多返回 40 条商品,最多支持 100 页即单次调用最大获取 4000 条商品数据(实际中可能因权重过滤更少)
字段限制部分敏感字段(如真实销量、商家联系方式)需额外申请基础调用仅返回 “销量区间”“店铺类型” 等非敏感信息

3. 核心参数解析

item_search_best 接口的参数分为必选参数可选参数,合理配置参数可大幅提升搜索精准度。

(1)必选参数

参数名类型说明示例
typeString接口类型标识,固定为 item_search_best"item_search_best"
keywordString搜索关键词(支持中文、英文、数字)"夏季连衣裙 显瘦"
pageInteger页码(1 ≤ page ≤ 100)1(默认第 1 页)
page_sizeInteger每页商品数量(10 ≤ page_size ≤ 40)40(默认 40 条 / 页)
client_idString开发者应用 ID(从拼多多开放平台获取)"your_client_id"
timestampLong时间戳(毫秒级,与当前时间差不超过 10 分钟)1718000000000
signString签名(按拼多多签名算法生成)见下文 “签名生成逻辑”

(2)可选参数(常用)

参数名类型说明示例
min_priceFloat最低价格(单位:元,过滤低于该价格的商品)50.0(仅返回 ≥50 元的商品)
max_priceFloat最高价格(单位:元,过滤高于该价格的商品)200.0(仅返回 ≤200 元的商品)
salesInteger销量筛选(1=10+,2=100+,3=1000+,4=10000+)3(仅返回销量 ≥1000 的商品)
areaString发货地筛选(格式:“省 - 市”,支持模糊匹配)"广东-广州"(仅返回广州发货的商品)
sort_typeInteger排序方式(0 = 默认权重排序,1 = 价格升序,2 = 价格降序,3 = 销量降序)3(按销量从高到低排序)
is_brandInteger品牌商品筛选(0 = 全部,1 = 仅品牌商品)1(仅返回品牌商品)

二、接口签名生成逻辑

拼多多开放平台采用 HMAC-SHA256 算法生成签名,确保请求合法性,签名生成步骤如下:


  1. 参数排序:将所有请求参数(含必选和可选参数)按 参数名 ASCII 码升序 排序(如 area 在 client_id 之前,keyword 在 page 之前)。

  2. 拼接参数串:按 “key=value” 格式拼接排序后的参数,参数之间无分隔符(如 area=广东-广州client_id=xxxkeyword=夏季连衣裙)。

  3. 拼接密钥:在参数串前后分别拼接开发者的 client_secret(从开放平台获取),形成最终待签名字符串(格式:client_secret + 参数串 + client_secret)。

  4. 生成签名:使用 HMAC-SHA256 算法对最终字符串进行加密,结果转为 小写字母,即为 sign 参数值。


示例
假设 client_secret=abc123,参数为 keyword=连衣裙&page=1&page_size=20,则:


  • 排序后参数:keyword=连衣裙page=1page_size=20

  • 参数串:keyword=连衣裙page=1page_size=20

  • 待签名字符串:abc123keyword=连衣裙page=1page_size=20abc123

  • 签名结果:通过 HMAC-SHA256 加密后得到小写字符串(如 a3f2d1e4b5c6...

三、返回数据结构解析

item_search_best 接口返回 JSON 格式数据,核心结构分为 基础信息 和 商品列表 两部分,以下是关键字段解析(仅列出常用字段):

1. 基础信息(外层字段)

字段名类型说明
codeInteger状态码(0 = 成功,非 0 = 失败)
msgString状态描述(失败时返回错误原因)
total_countInteger符合条件的商品总数(最大返回 4000)
pageInteger当前页码
page_sizeInteger当前页商品数量
itemsObject商品列表容器(核心字段)

2. 商品列表(items.list 字段)

字段名类型说明
goods_idString商品 ID(唯一标识,用于调用商品详情接口)
goods_nameString商品标题(含促销信息,如 “买一送一”)
goods_imageString商品主图 URL
goods_priceFloat商品现价(单位:元)
market_priceFloat商品原价(单位:元,可能为 0 表示无原价)
salesInteger商品销量(部分场景返回销量区间,如 1000+ 对应 1000)
seller_idString商家 ID
seller_nameString商家名称
shop_typeString店铺类型(“旗舰店”“专营店”“普通店”)
areaString发货地(格式:“省 - 市”)
ratingFloat商品评分(0-5 分,部分商品无评分时为 0)
coupon_infoString优惠券信息(如 “满 100 减 20”,无优惠券时为空)
detail_urlString商品详情页 URL(H5 链接,可直接访问)

四、Python 实现方案

以下是 item_search_best 接口的完整 Python 实现,包含 基础调用框架签名生成数据解析异常处理 及 可视化分析 功能:

import requests import time import hmac import hashlib import json import logging import pandas as pd import matplotlib.pyplot as plt import numpy as np from datetime import datetime from collections import defaultdict from typing import Dict, List, Optional, Union

配置日志

logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)


class PinduoduoItemSearchBest:
"""拼多多 item_search_best 接口封装类,支持搜索、数据解析与分析"""


def init(self, client_id: str, client_secret: str):
"""
初始化拼多多 API 客户端
:param client_id: 开发者应用 ID(从拼多多开放平台获取)
:param client_secret: 开发者应用密钥(从拼多多开放平台获取)
"""
self.client_id = client_id
self.client_secret = client_secret
self.gateway_url = "https://gw-api.pinduoduo.com/api/router" # 拼多多 API 网关
self.session = requests.Session ()
self.session.headers.update ({
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"User-Agent": "PDDItemSearchBest/1.0.0 (Python)"
})

调用频率控制(默认 10 次 / 分钟,即每 6 秒 1 次)

self.call_interval = 6 # 调用间隔(秒)
self.last_call_time = 0 # 上次调用时间(毫秒)


def _generate_sign (self, params: Dict) -> str:
"""
生成签名(HMAC-SHA256 算法)
:param params: 请求参数字典
:return: 签名字符串(小写)
"""

1. 按参数名 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. 拼接 client_secret 生成待签名字符串

sign_str = f"{self.client_secret}{param_str}{self.client_secret}"

4. HMAC-SHA256 加密并转为小写

sign = hmac.new(
key=self.client_secret.encode("utf-8"),
msg=sign_str.encode("utf-8"),
digestmod=hashlib.sha256
).hexdigest().lower()
return sign


def _check_rate_limit (self) -> None:
"""控制调用频率,避免超限"""
current_time = time.time () * 1000 # 转为毫秒
time_since_last_call = current_time - self.last_call_time


if time_since_last_call < self.call_interval * 1000:
sleep_time = (self.call_interval * 1000 - time_since_last_call) / 1000
logging.warning (f"调用频率超限,等待 {sleep_time:.1f} 秒")
time.sleep (sleep_time)


self.last_call_time = time.time() * 1000


def call_item_search_best (self, keyword: str, page: int = 1, page_size: int = 40,
filters: Optional [Dict] = None) -> Optional [Dict]:
"""
调用 item_search_best 接口
:param keyword: 搜索关键词
:param page: 页码(1 ≤ page ≤ 100)
:param page_size: 每页数量(10 ≤ page_size ≤ 40)
:param filters: 可选筛选参数(如 min_price、sales、area 等)
:return: 接口返回结果(字典格式),失败返回 None
"""

1. 基础参数构建

base_params = {
"type": "item_search_best",
"keyword": keyword.strip (),
"page": page,
"page_size": page_size,
"client_id": self.client_id,
"timestamp": int (time.time () * 1000) # 毫秒级时间戳
}

2. 合并可选筛选参数

if filters and isinstance(filters, Dict):

过滤空值参数

valid_filters = {k: v for k, v in filters.items() if v is not None}
base_params.update(valid_filters)

3. 生成签名

base_params["sign"] = self._generate_sign(base_params)

4. 频率控制

self._check_rate_limit()


try:

5. 发送请求

response = self.session.post (self.gateway_url, data=base_params, timeout=15)
response.raise_for_status () # 抛出 HTTP 错误(如 404、500)

6. 解析响应

result = response.json()

7. 处理状态码

if result.get ("code") != 0:
logging.error (f"接口调用失败:code={result.get ('code')}, msg={result.get ('msg')}")
return None


logging.info(f"成功获取第 {page} 页商品,共 {result.get ('total_count', 0)} 件符合条件")
return result


except requests.exceptions.RequestException as e:
logging.error (f"请求异常:{str (e)}")
return None
except json.JSONDecodeError:
logging.error (f"响应解析失败:{response.text [:200]}...")
return None


def batch_get_products (self, keyword: str, max_pages: int = 5, page_size: int = 40,
filters: Optional [Dict] = None) -> List [Dict]:
"""
批量获取多页商品数据
:param keyword: 搜索关键词
:param max_pages: 最大获取页数(1 ≤ max_pages ≤ 100)
:param page_size: 每页数量
:param filters: 可选筛选参数
:return: 所有商品列表(字典列表)
"""
all_products = []
max_pages = min (max_pages, 100) # 限制最大页数为 100


for page in range(1, max_pages + 1):
logging.info(f"正在获取第 {page}/{max_pages} 页商品...")
result = self.call_item_search_best (keyword, page, page_size, filters)


if not result or "items" not in result or "list" not in result ["items"]:
logging.warning (f"第 {page} 页无商品数据,停止批量获取")
break

提取商品列表并添加页码信息

page_products = result ["items"]["list"]
for product in page_products:
product ["page"] = page # 标记商品所在页码
all_products.extend (page_products)

若当前页商品数量不足 page_size,说明已无更多数据

if len(page_products) < page_size:
logging.info(f"第 {page} 页商品数量不足 {page_size} 条,停止批量获取")
break


logging.info(f"批量获取完成,共获取 {len (all_products)} 件商品")
return all_products


def analyze_product_data (self, products: List [Dict]) -> Dict:
"""
分析商品数据(价格分布、销量分布、店铺类型占比等)
:param products: 商品列表(从 batch_get_products 获得)
:return: 分析结果字典
"""
if not products:
logging.warning ("无商品数据可分析")
return {}

1. 价格分析

prices = [p.get("goods_price", 0.0) for p in products if p.get("goods_price") > 0]
price_stats = {}
if prices:
price_stats = {
"min": round(min(prices), 2),
"max": round(max(prices), 2),
"avg": round(sum(prices) / len(prices), 2),
"median": round(np.median(prices), 2),
"count": len(prices)
}

2. 销量分析

sales = [p.get("sales", 0) for p


群贤毕至

访客