微店平台商品详情接口技术实现:从接口解析到数据结构化全方案

发布于:2025-09-11 ⋅ 阅读:(25) ⋅ 点赞:(0)

微店作为国内主流的社交电商平台,其商品详情数据包含价格、库存、规格、图文描述等核心商业信息,是电商分析、竞品调研和供应链整合的重要基础。与其他平台相比,微店商品详情接口具有独特的签名机制和数据结构。本文将系统讲解微店商品详情接口的技术实现,重点解决接口参数构造、签名生成、动态数据解析等核心问题,提供一套合规、可落地的技术方案。

一、微店商品详情接口架构与合规要点

接口核心信息

微店商品详情数据通过 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 或收到平台警告时,应立即停止调用并检查原因

通过本文提供的技术方案,开发者可以快速实现微店商品详情接口的调用与数据处理。该方案包含完整的签名生成、参数构造、响应解析和批量处理功能,可直接集成到各类电商分析和管理系统中。在实际应用中,需特别注意控制请求频率,做好错误处理和缓存策略,确保系统稳定、合规运行。


网站公告

今日签到

点亮在社区的每一天
去签到