微店作为国内主流的社交电商平台,其商品详情数据包含价格、库存、规格、图文描述等核心商业信息,是电商分析、竞品调研和供应链整合的重要基础。与其他平台相比,微店商品详情接口具有独特的签名机制和数据结构。本文将系统讲解微店商品详情接口的技术实现,重点解决接口参数构造、签名生成、动态数据解析等核心问题,提供一套合规、可落地的技术方案。
一、微店商品详情接口架构与合规要点
接口核心信息
微店商品详情数据通过 API 接口和页面渲染两种方式呈现,本文聚焦于开放平台 API 接口的规范调用:
- 接口名称:获取商品详情
- 接口地址:
https://api.weidian.com/item/get
- 请求方式:HTTP POST
- 权限要求:需在微店开放平台创建应用并获取授权
- 数据格式:请求与响应均为 JSON 格式
典型应用场景
- 多平台商品同步系统:将微店商品信息同步至其他销售渠道
- 电商数据分析工具:采集商品价格、销量等数据进行市场分析
- 供应链管理系统:根据商品详情数据优化采购策略
- 竞品监控系统:跟踪竞争对手商品信息变化
合规要点
- 严格遵守《微店开放平台服务协议》,仅调用已授权接口
- 单 IP 请求频率控制在每秒 1 次以内,每日上限 1000 次
- 采集数据仅用于自身业务分析,不得用于商业竞争
- 尊重商家知识产权,不盗用商品图片和描述信息
接口调用流程
plaintext
应用授权 → 签名参数生成 → 接口请求 → 数据解密(如需要) → 响应解析 → 结构化存储
点击获取key和secret
二、接口参数详解
公共请求参数
参数名 | 类型 | 说明 |
---|---|---|
appkey | String | 应用唯一标识,在开放平台应用管理中获取 |
method | String | 接口方法名,固定为item.get |
timestamp | Long | 时间戳,单位为秒,需与服务器时间误差在 5 分钟内 |
version | String | 接口版本,当前最新为1.0 |
format | String | 响应格式,固定为json |
sign | String | 签名结果,通过微店签名算法生成 |
access_token | String | 访问令牌,通过授权流程获取 |
业务请求参数
参数名 | 类型 | 说明 |
---|---|---|
item_id | Long | 商品 ID,微店商品的唯一标识 |
fields | String | 需要返回的字段列表,多个用逗号分隔,如item_id,title,price |
常用返回字段说明
字段名 | 类型 | 说明 |
---|---|---|
item_id | Long | 商品 ID |
title | String | 商品标题 |
price | Float | 商品售价 |
original_price | Float | 商品原价 |
stock | Int | 商品库存 |
sales | Int | 商品销量 |
images | Array | 商品图片 URL 列表 |
detail | String | 商品详情 HTML |
sku_list | Array | 商品规格列表 |
category_id | Int | 商品分类 ID |
created | Int | 商品创建时间戳 |
updated | Int | 商品更新时间戳 |
三、核心技术实现
1. 签名工具类(遵循微店签名规范)
微店采用 MD5 签名算法,签名过程需严格按照参数排序、拼接、加密的流程进行:
python
运行
import hashlib
import urllib.parse
from collections import OrderedDict
class WeidianSignUtil:
"""微店开放平台签名工具类"""
@staticmethod
def generate_sign(params, app_secret):
"""
生成签名
:param params: 参数字典
:param app_secret: 应用密钥
:return: 签名字符串
"""
# 1. 排除sign参数(如果存在)
if 'sign' in params:
del params['sign']
# 2. 参数按字母顺序排序
sorted_params = OrderedDict(sorted(params.items(), key=lambda x: x[0]))
# 3. 拼接为key=value形式,并用&连接
sign_str = '&'.join([f"{k}={urllib.parse.quote(str(v), safe='')}" for k, v in sorted_params.items()])
# 4. 拼接app_secret
sign_str += app_secret
# 5. MD5加密并转为大写
return hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper()
2. 微店商品详情接口客户端
封装接口调用的完整流程,包括参数构造、签名生成、HTTP 请求及响应处理:
python
运行
import time
import requests
import json
class WeidianItemClient:
"""微店商品详情接口客户端"""
def __init__(self, appkey, app_secret, access_token):
self.appkey = appkey
self.app_secret = app_secret
self.access_token = access_token
self.api_url = "https://api.weidian.com/item/get"
def _get_common_params(self):
"""生成公共请求参数"""
return {
"appkey": self.appkey,
"method": "item.get",
"timestamp": int(time.time()),
"version": "1.0",
"format": "json",
"access_token": self.access_token
}
def get_item_detail(self, item_id, fields=None):
"""
获取商品详情
:param item_id: 商品ID
:param fields: 需要返回的字段列表,None则返回全部字段
:return: 商品详情字典
"""
# 1. 构造完整参数
params = self._get_common_params()
# 2. 添加业务参数
business_params = {
"item_id": item_id
}
if fields:
business_params["fields"] = fields
# 微店API要求业务参数放在param_json中
params["param_json"] = json.dumps(business_params)
# 3. 生成签名
params["sign"] = WeidianSignUtil.generate_sign(params, self.app_secret)
# 4. 发送请求
try:
response = requests.post(
self.api_url,
data=params,
headers={
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
},
timeout=15
)
# 5. 解析响应
result = response.json()
# 6. 处理错误响应
if result.get("status") != 0:
raise Exception(f"接口调用失败: {result.get('msg', '')} (错误码: {result.get('status', '')})")
return result.get("result", {})
except Exception as e:
print(f"接口调用异常: {str(e)}")
return None
3. 商品详情数据解析器
对接口返回的复杂数据结构进行解析,提取核心信息并进行结构化处理:
python
运行
import re
from datetime import datetime
from bs4 import BeautifulSoup
class WeidianItemParser:
"""微店商品详情数据解析器"""
@staticmethod
def parse_item_detail(raw_data):
"""
解析商品详情数据
:param raw_data: 接口返回的原始数据
:return: 结构化的商品信息字典
"""
if not raw_data:
return None
# 基础信息
basic_info = {
"item_id": raw_data.get("item_id", ""),
"title": raw_data.get("title", ""),
"price": float(raw_data.get("price", 0)),
"original_price": float(raw_data.get("original_price", 0)),
"stock": int(raw_data.get("stock", 0)),
"sales": int(raw_data.get("sales", 0)),
"category_id": raw_data.get("category_id", ""),
"category_name": raw_data.get("category_name", ""),
"created_time": datetime.fromtimestamp(raw_data.get("created", 0)).strftime("%Y-%m-%d %H:%M:%S") if raw_data.get("created") else "",
"updated_time": datetime.fromtimestamp(raw_data.get("updated", 0)).strftime("%Y-%m-%d %H:%M:%S") if raw_data.get("updated") else ""
}
# 解析商品图片
images = WeidianItemParser._parse_images(raw_data.get("images", []))
# 解析商品规格
sku_list = WeidianItemParser._parse_sku(raw_data.get("sku_list", []))
# 解析商品详情
detail = WeidianItemParser._parse_detail(raw_data.get("detail", ""))
# 组合结果
return {
**basic_info,
"images": images,
"sku_list": sku_list,
"detail": detail
}
@staticmethod
def _parse_images(images_data):
"""解析商品图片"""
if not images_data:
return []
# 处理不同格式的图片数据
if isinstance(images_data, list):
return [img.get("url", "") for img in images_data if img.get("url")]
elif isinstance(images_data, str):
# 处理逗号分隔的URL字符串
return [url.strip() for url in images_data.split(",") if url.strip()]
return []
@staticmethod
def _parse_sku(sku_data):
"""解析商品规格"""
if not sku_data:
return []
sku_list = []
for sku in sku_data:
# 解析规格属性
properties = []
if "props" in sku:
for prop in sku["props"]:
properties.append({
"name": prop.get("name", ""),
"value": prop.get("value", "")
})
sku_list.append({
"sku_id": sku.get("sku_id", ""),
"properties": properties,
"price": float(sku.get("price", 0)),
"stock": int(sku.get("stock", 0)),
"sales": int(sku.get("sales", 0)),
"image": sku.get("image", "")
})
return sku_list
@staticmethod
def _parse_detail(detail_html):
"""解析商品详情HTML"""
if not detail_html:
return {"text": "", "images": []}
# 提取文本内容
soup = BeautifulSoup(detail_html, "html.parser")
text_content = soup.get_text().strip()
# 提取图片URL
images = []
for img in soup.find_all("img"):
img_url = img.get("src", "")
if img_url:
# 补全相对路径
if img_url.startswith("//"):
img_url = "https:" + img_url
elif img_url.startswith("/"):
img_url = "https://weidian.com" + img_url
images.append(img_url)
return {
"text": text_content,
"images": list(set(images)) # 去重
}
4. 批量查询与缓存工具
实现商品详情的批量查询功能,并提供本地缓存机制减少重复请求:
python
运行
import time
import json
import os
from datetime import datetime, timedelta
from concurrent.futures import ThreadPoolExecutor, as_completed
class WeidianItemBatchFetcher:
"""微店商品详情批量查询工具"""
def __init__(self, appkey, app_secret, access_token, cache_dir="./weidian_cache", cache_expire=86400):
self.client = WeidianItemClient(appkey, app_secret, access_token)
self.parser = WeidianItemParser()
self.cache_dir = cache_dir
self.cache_expire = cache_expire # 缓存过期时间(秒),默认24小时
# 创建缓存目录
if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir)
def batch_get_items(self, item_ids, fields=None, max_workers=2):
"""
批量获取商品详情
:param item_ids: 商品ID列表
:param fields: 需要返回的字段列表
:param max_workers: 并发数
:return: 商品详情字典,key为item_id
"""
results = {}
futures = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# 提交查询任务
for item_id in item_ids:
# 先检查缓存
cached_data = self._get_cached_item(item_id)
if cached_data:
results[item_id] = cached_data
continue
# 缓存未命中,提交任务
futures.append(executor.submit(
self._fetch_single_item,
item_id,
fields
))
time.sleep(1) # 控制请求频率,每秒不超过1次
# 处理查询结果
for future in as_completed(futures):
item_id, data = future.result()
if data:
results[item_id] = data
# 保存到缓存
self._cache_item(item_id, data)
return results
def _fetch_single_item(self, item_id, fields):
"""查询单个商品详情"""
try:
print(f"获取商品 {item_id} 详情...")
raw_data = self.client.get_item_detail(item_id, fields)
if raw_data:
parsed_data = self.parser.parse_item_detail(raw_data)
return item_id, parsed_data
return item_id, None
except Exception as e:
print(f"商品 {item_id} 查询失败: {str(e)}")
return item_id, None
def _get_cached_item(self, item_id):
"""从缓存获取商品详情"""
cache_file = os.path.join(self.cache_dir, f"{item_id}.json")
if not os.path.exists(cache_file):
return None
# 检查缓存是否过期
file_mtime = os.path.getmtime(cache_file)
if time.time() - file_mtime > self.cache_expire:
os.remove(cache_file) # 删除过期缓存
return None
# 读取缓存数据
try:
with open(cache_file, "r", encoding="utf-8") as f:
print(f"从缓存获取商品 {item_id} 详情")
return json.load(f)
except:
return None
def _cache_item(self, item_id, data):
"""缓存商品详情"""
cache_file = os.path.join(self.cache_dir, f"{item_id}.json")
try:
with open(cache_file, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"缓存商品 {item_id} 失败: {str(e)}")
四、完整使用示例
1. 单商品详情查询示例
python
运行
def single_item_demo():
# 替换为自己的应用信息
APPKEY = "your_appkey"
APP_SECRET = "your_app_secret"
ACCESS_TOKEN = "your_access_token"
# 初始化客户端和解析器
client = WeidianItemClient(APPKEY, APP_SECRET, ACCESS_TOKEN)
parser = WeidianItemParser()
# 要查询的商品ID
item_id = 123456789
# 需要返回的字段(可选)
fields = "item_id,title,price,original_price,stock,sales,images,detail,sku_list,created,updated"
# 调用接口
raw_data = client.get_item_detail(item_id, fields)
if raw_data:
# 解析结果
item_detail = parser.parse_item_detail(raw_data)
# 打印基本信息
print(f"商品ID: {item_detail['item_id']}")
print(f"商品标题: {item_detail['title']}")
print(f"售价: ¥{item_detail['price']} (原价: ¥{item_detail['original_price']})")
print(f"库存: {item_detail['stock']} 件")
print(f"销量: {item_detail['sales']} 件")
print(f"分类: {item_detail['category_name']}")
print(f"创建时间: {item_detail['created_time']}")
print(f"更新时间: {item_detail['updated_time']}")
# 打印图片数量
print(f"\n商品图片数量: {len(item_detail['images'])}")
# 打印规格信息
if item_detail['sku_list']:
print(f"\n规格数量: {len(item_detail['sku_list'])}")
for i, sku in enumerate(item_detail['sku_list'], 1):
props = ", ".join([f"{p['name']}:{p['value']}" for p in sku['properties']])
print(f"规格 {i}: {props} - 价格: ¥{sku['price']} - 库存: {sku['stock']}")
# 打印详情信息
print(f"\n详情文本长度: {len(item_detail['detail']['text'])} 字符")
print(f"详情图片数量: {len(item_detail['detail']['images'])}")
if __name__ == "__main__":
single_item_demo()
2. 批量商品查询与数据导出示例
python
运行
import csv
def batch_item_demo():
# 替换为自己的应用信息
APPKEY = "your_appkey"
APP_SECRET = "your_app_secret"
ACCESS_TOKEN = "your_access_token"
# 初始化批量查询工具
batch_fetcher = WeidianItemBatchFetcher(APPKEY, APP_SECRET, ACCESS_TOKEN)
# 要查询的商品ID列表
item_ids = [123456789, 987654321, 112233445, 556677889]
# 批量查询
results = batch_fetcher.batch_get_items(item_ids)
# 打印结果统计
print(f"\n批量查询完成,共查询 {len(item_ids)} 个商品,成功获取 {len(results)} 个")
# 导出为CSV
export_to_csv(results, "weidian_items.csv")
def export_to_csv(items, filename):
"""将商品数据导出为CSV文件"""
if not items:
return
# 写入CSV
with open(filename, "w", encoding="utf-8-sig", newline="") as f:
writer = csv.writer(f)
# 表头
headers = [
"商品ID", "标题", "售价", "原价", "库存", "销量",
"分类", "创建时间", "更新时间", "图片数量", "规格数量", "详情图片数量"
]
writer.writerow(headers)
# 数据行
for item in items.values():
row = [
item["item_id"],
item["title"],
item["price"],
item["original_price"],
item["stock"],
item["sales"],
item["category_name"],
item["created_time"],
item["updated_time"],
len(item["images"]),
len(item["sku_list"]),
len(item["detail"]["images"])
]
writer.writerow(row)
print(f"数据已导出至 {filename}")
if __name__ == "__main__":
batch_item_demo()
五、接口调用优化与风险提示
1. 常见错误码及解决方案
错误码 | 说明 | 解决方案 |
---|---|---|
400 | 参数错误 | 检查 item_id 是否有效,字段名是否正确 |
401 | 未授权 | 检查 access_token 是否过期或有效 |
403 | 权限不足 | 确认应用是否有访问该商品的权限 |
404 | 商品不存在 | 确认 item_id 是否正确,商品可能已下架 |
429 | 请求频率过高 | 降低请求频率,增加请求间隔 |
500 | 服务器错误 | 稍后重试,记录错误日志 |
2. 性能优化策略
- 合理设置 fields 参数:只请求需要的字段,减少数据传输量
- 缓存机制:对不常变动的商品信息进行本地缓存,减少接口调用
- 批量处理:多个商品查询时使用批量查询工具,控制并发数
- 增量更新:定期只更新有变化的商品信息,通过 updated 字段判断
3. 合规使用与风险提示
- 所有接口调用必须遵守微店开放平台的《API 使用规范》
- 不得利用接口获取未公开的商品信息或进行恶意查询
- access_token 应妥善保管,避免泄露,定期更新
- 商业应用前需获得微店平台的书面授权,明确数据使用范围
- 当接口返回 403 或收到平台警告时,应立即停止调用并检查原因
通过本文提供的技术方案,开发者可以快速实现微店商品详情接口的调用与数据处理。该方案包含完整的签名生成、参数构造、响应解析和批量处理功能,可直接集成到各类电商分析和管理系统中。在实际应用中,需特别注意控制请求频率,做好错误处理和缓存策略,确保系统稳定、合规运行。