前言 在商品数据同步业务中,唯品会详情接口最大难点不是基础请求,而是嵌套多层的 SKU 规格、动态促销标签与限时价格。常规直接读取原始返回 JSON 的写法,经常出现规格数组入库困难、活动价和原价混淆、下架商品残留脏数据等问题。本文不只是简单演示接口调用,重点封装一套 SKU 扁平化、促销字段分离、异常重试的工具类,适用于商品数据同步、比价业务。 前置准备 Python3 环境,依赖 requests 库。接口存在会话校验,需要维持请求会话;平台会校验请求头,缺少 Referer 或者 UA 会直接返回数据为空。批量查询时必须增加请求间隔,高频调用会触发 IP 限流。 完整代码示例 核心逻辑解析 会话复用:使用 Session 保持 Cookie,减少每次请求握手开销,降低风控识别概率。 SKU 扁平化处理:将多层嵌套的 SKU 规格提取为一维数组,方便直接写入数据库,避免嵌套 JSON 入库解析麻烦。 促销标签独立存放:把活动标签单独拆分,和基础价格隔离,防止业务误把临时促销价作为日常售价。 重试机制:网络抖动时自动重试,减少单次请求失败造成的数据丢失。 线上踩坑要点 商品状态校验:下架商品接口依然会返回基础标题,必须通过 status 字段判断在售状态,避免同步无效商品。 图片地址:详情描述内图片为相对路径,业务使用时需要拼接域名,否则图片无法正常加载。 价格类型:价格字段为字符串类型,入库前建议转为浮点,空价格需要做兜底处理,防止程序抛出类型异常。 限流策略:单次查询间隔建议大于 1 秒,大规模同步建议使用代理 IP 轮换。
python
import requests
import time
class VipDetailClient:
def __init__(self):
self.session = requests.Session()
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Referer": "https://www.vip.com/"
}
self.retry_times = 2
self.delay = 1.3
def get_goods_detail(self, goods_id):
"""根据商品ID获取详情,扁平化SKU数据"""
api_url = "https://api.vip.com/item/detail"
params = {
"itemId": goods_id
}
for i in range(self.retry_times):
try:
resp = self.session.get(api_url, params=params, headers=self.headers, timeout=12)
time.sleep(self.delay)
if resp.status_code != 200:
return {"code": resp.status_code, "data": None, "msg": "http请求失败"}
res_json = resp.json()
raw_data = res_json.get("data", {})
sku_list = []
# 扁平化SKU规格
for sku in raw_data.get("skuList", []):
sku_info = {
"sku_id": sku.get("skuId"),
"spec_name": sku.get("spec", ""),
"sale_price": sku.get("salePrice"),
"market_price": sku.get("marketPrice"),
"stock_num": sku.get("stock", 0)
}
sku_list.append(sku_info)
detail_result = {
"goods_id": raw_data.get("itemId"),
"title": raw_data.get("title", "").strip(),
"brand_name": raw_data.get("brandName", ""),
"main_images": raw_data.get("picList", []),
"desc_html": raw_data.get("description", ""),
"promotion_tags": raw_data.get("promotionTagList", []),
"sku_list": sku_list,
"is_on_sale": raw_data.get("status") == 1
}
return {"code":0, "data":detail_result, "msg":"success"}
except Exception as e:
if i < self.retry_times -1:
time.sleep(2)
continue
return {"code":-1, "data":None, "msg":str(e)}
if __name__ == "__main__":
client = VipDetailClient()
item_id = "123456789"
ret = client.get_goods_detail(item_id)
if ret["code"] ==0:
item = ret["data"]
print("商品标题:", item["title"])
print("SKU数量:", len(item["sku_list"]))
else:
print("获取详情失败:", ret["msg"])