[item_review_show]
接口是淘宝开放平台提供的用于获取商品买家秀内容的接口。买家秀作为消费者购物决策的重要参考,包含了丰富的用户生成内容,如晒图、视频、评价文字等,对于电商分析、商品优化、用户体验提升等场景具有重要价值。 该接口返回的数据特点:
- 包含真实买家上传的商品实拍图和视频
- 带有详细的使用体验描述和评价
- 包含购买规格、使用场景等信息
- 部分数据带有其他用户的互动(点赞、评论)信息
接口核心参数与响应结构
请求参数
参数名 | 类型 | 是否必填 | 说明 |
---|---|---|---|
item_id |
String | 是 | 商品 ID |
page |
Integer | 否 | 页码,默认 1 |
page_size |
Integer | 否 | 每页条数,默认 20,最大 50 |
sort |
String | 否 | 排序方式:newest (最新)、hot (最热) |
has_image |
Boolean | 否 | 是否只看有图评价,默认 false |
has_video |
Boolean | 否 | 是否只看有视频评价,默认 false |
appkey |
String | 是 | 应用密钥 |
session |
String | 是 | 用户会话 |
响应核心字段
-
分页信息:总条数、总页数、当前页码
-
买家秀列表:每条包含
- 基础信息:评价 ID、用户昵称、用户等级
- 内容信息:评价文字、评分、发布时间
- 多媒体:图片 URL 列表、视频 URL
- 商品信息:购买规格、使用感受
- 互动数据:点赞数、评论数
Python 实现 import requests import time import json import logging import re import os from typing import Dict, Optional, List from requests.exceptions import RequestException from PIL import Image from io import BytesIO import matplotlib.pyplot as plt import numpy as np from collections import defaultdict
配置日志 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" )
class TaobaoItemReviewShowAPI: def init (self, appkey: str, session: str): """ 初始化淘宝买家秀API客户端 :param appkey: 淘宝开放平台appkey :param session: 用户会话 """ self.appkey = appkey self.session = session self.base_url = "eco.taobao.com/router/rest" self.session = requests.Session() self.session.headers.update({ "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" })
python
def get_item_reviews(self,
item_id: str,
page: int = 1,
page_size: int = 20,
sort: str = "newest",
has_image: bool = False,
has_video: bool = False) -> Optional[Dict]:
"""
获取商品买家秀
:param item_id: 商品ID
:param page: 页码
:param page_size: 每页条数
:param sort: 排序方式
:param has_image: 是否只看有图评价
:param has_video: 是否只看有视频评价
:return: 买家秀数据
"""
# 验证参数
valid_sorts = ["newest", "hot"]
if sort not in valid_sorts:
logging.error(f"无效的排序方式: {sort},支持: {valid_sorts}")
return None
if page_size < 1 or page_size > 50:
logging.error(f"每页条数必须在1-50之间,当前为: {page_size}")
return None
# 构建参数
params = {
"method": "taobao.item.review.show",
"app_key": self.appkey,
"session": self.session,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"format": "json",
"v": "2.0",
"item_id": item_id,
"page": page,
"page_size": page_size,
"sort": sort,
"has_image": "true" if has_image else "false",
"has_video": "true" if has_video else "false"
}
try:
response = self.session.post(self.base_url, data=params, timeout=15)
response.raise_for_status()
result = response.json()
if "error_response" in result:
logging.error(f"获取买家秀失败: {result['error_response']['msg']} (错误码: {result['error_response']['code']})")
return None
reviews_data = result.get("item_review_show_response", {}).get("reviews", {})
if not reviews_data:
logging.warning("未获取到买家秀数据")
return None
# 格式化买家秀数据
return self._format_review_data(reviews_data)
except RequestException as e:
logging.error(f"获取买家秀请求异常: {str(e)}")
return None
except json.JSONDecodeError:
logging.error(f"买家秀响应解析失败: {response.text[:200]}...")
return None
def _format_review_data(self, review_data: Dict) -> Dict:
"""格式化买家秀数据"""
# 分页信息
pagination = {
"total_count": int(review_data.get("total", 0)),
"total_pages": (int(review_data.get("total", 0)) + int(review_data.get("page_size", 20)) - 1) // int(review_data.get("page_size", 20)),
"current_page": int(review_data.get("page", 1)),
"page_size": int(review_data.get("page_size", 20))
}
# 格式化买家秀列表
reviews = []
for review in review_data.get("review_list", []):
# 处理评价内容
content = self._clean_text(review.get("content", ""))
# 处理图片
images = []
if review.get("pics"):
images = [pic.get("url") for pic in review.get("pics") if pic.get("url")]
# 处理视频
video = None
if review.get("video"):
video = {
"url": review["video"].get("url"),
"duration": review["video"].get("duration"),
"cover_url": review["video"].get("cover_url")
}
# 处理规格信息
sku_info = {}
if review.get("sku"):
sku_info = {
"properties": review.get("sku", ""),
"property_names": review.get("sku_info", "")
}
reviews.append({
"review_id": review.get("review_id"),
"user": {
"nickname": review.get("nick"),
"avatar": review.get("avatar"),
"level": review.get("user_level")
},
"content": content,
"rating": int(review.get("rate", 0)), # 评分 1-5
"created_time": review.get("created"),
"images": images,
"video": video,
"sku_info": sku_info,
"likes": int(review.get("like_count", 0)),
"comments": int(review.get("comment_count", 0)),
"useful": int(review.get("useful", 0)),
"days_used": review.get("days", "") # 使用多少天后评价
})
return {
"pagination": pagination,
"reviews": reviews,
"raw_data": review_data # 保留原始数据
}
def get_all_reviews(self, item_id: str, max_pages: int = 10, **kwargs) -> List[Dict]:
"""
获取多页买家秀
:param item_id: 商品ID
:param max_pages: 最大页数限制
:return: 所有买家秀列表
"""
all_reviews = []
page = 1
while page <= max_pages:
logging.info(f"获取第 {page} 页买家秀")
result = self.get_item_reviews(
item_id=item_id,
page=page,
page_size=50, # 使用最大页大小
** kwargs
)
if not result or not result["reviews"]:
break
all_reviews.extend(result["reviews"])
# 检查是否已到最后一页
if page >= result["pagination"]["total_pages"]:
break
page += 1
# 控制请求频率
time.sleep(2)
logging.info(f"共获取到 {len(all_reviews)} 条买家秀")
return all_reviews
def _clean_text(self, text: str) -> str:
"""清理文本内容"""
if not text:
return ""
# 去除HTML标签
clean = re.sub(r'<.*?>', ' ', text)
# 去除多余空格和换行
clean = re.sub(r'\s+', ' ', clean).strip()
return clean
def download_image(self, url: str, save_path: str) -> bool:
"""下载买家秀图片"""
try:
response = self.session.get(url, timeout=10)
response.raise_for_status()
# 保存图片
with open(save_path, 'wb') as f:
f.write(response.content)
return True
except Exception as e:
logging.error(f"下载图片失败 {url}: {str(e)}")
return False
def analyze_reviews(self, reviews: List[Dict]) -> Dict:
"""分析买家秀数据"""
if not reviews:
return {}
total = len(reviews)
# 评分分析
rating_analysis = self._analyze_ratings(reviews)
# 多媒体分析
media_analysis = self._analyze_media(reviews)
# 关键词分析
keyword_analysis = self._analyze_keywords(reviews)
# 规格分析
sku_analysis = self._analyze_skus(reviews)
# 热门买家秀
top_reviews = sorted(reviews, key=lambda x: x["likes"], reverse=True)[:10]
return {
"total_reviews": total,
"rating_analysis": rating_analysis,
"media_analysis": media_analysis,
"keyword_analysis": keyword_analysis,
"sku_analysis": sku_analysis,
"top_reviews": top_reviews
}
def _analyze_ratings(self, reviews: List[Dict]) -> Dict:
"""分析评分分布"""
rating_counts = defaultdict(int)
total_rating = 0
rated_count = 0
for review in reviews:
rating = review["rating"]
if rating > 0:
rating_counts[rating] += 1
total_rating += rating
rated_count += 1
avg_rating = round(total_rating / rated_count, 1) if rated_count > 0 else 0
return {
"counts": dict(rating_counts),
"average": avg_rating,
"distribution": {k: round(v/rated_count*100, 1) for k, v in rating_counts.items()} if rated_count > 0 else {}
}
def _analyze_media(self, reviews: List[Dict]) -> Dict:
"""分析多媒体内容"""
has_image_count = 0
has_video_count = 0
total_images = 0
for review in reviews:
if review["images"]:
has_image_count += 1
total_images += len(review["images"])
if review["video"]:
has_video_count += 1
return {
"has_image_ratio": round(has_image_count / len(reviews) * 100, 1),
"has_video_ratio": round(has_video_count / len(reviews) * 100, 1),
"avg_images_per_review": round(total_images / len(reviews), 1) if len(reviews) > 0 else 0
}
def _analyze_keywords(self, reviews: List[Dict]) -> Dict:
"""分析评价关键词"""
# 简单关键词提取,实际应用中可使用更复杂的NLP方法
keywords = {
"质量": ["质量", "材质", "做工", "品质"],
"外观": ["外观", "颜色", "款式", "样子", "好看"],
"尺寸": ["尺寸", "大小", "尺码", "合适", "合身"],
"性价比": ["性价比", "价格", "值", "划算"],
"物流": ["物流", "快递", "速度", "快", "慢"],
"服务": ["服务", "客服", "态度", "耐心"]
}
keyword_counts = defaultdict(int)
for review in reviews:
content = review["content"].lower()
for category, kws in keywords.items():
for kw in kws:
if kw in content:
keyword_counts[category] += 1
break # 每个类别只计数一次
# 按出现次数排序
sorted_keywords = sorted(keyword_counts.items(), key=lambda x: x[1], reverse=True)
return {
"counts": dict(keyword_counts),
"sorted": sorted_keywords,
"total_mentioned": sum(keyword_counts.values())
}
def _analyze_skus(self, reviews: List[Dict]) -> Dict:
"""分析规格购买分布"""
sku_counts = defaultdict(int)
for review in reviews:
if review["sku_info"]["property_names"]:
sku_name = review["sku_info"]["property_names"]
sku_counts[sku_name] += 1
# 按购买次数排序
sorted_skus = sorted(sku_counts.items(), key=lambda x: x[1], reverse=True)
return {
"counts": dict(sku_counts),
"sorted": sorted_skus[:10], # 前10个热门规格
"total_sku_types": len(sku_counts)
}
def visualize_analysis(self, analysis: Dict, output_dir: str = "review_analysis") -> None:
"""可视化分析结果"""
# 创建输出目录
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False
# 1. 评分分布饼图
plt.figure(figsize=(10, 6))
ratings = analysis["rating_analysis"]["counts"]
if ratings:
labels = [f"{k}星" for k in ratings.keys()]
sizes = list(ratings.values())
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
plt.title(f"买家秀评分分布 (平均评分: {analysis['rating_analysis']['average']})")
plt.axis('equal')
plt.tight_layout()
plt.savefig(f"{output_dir}/rating_distribution.png")
plt.close()
# 2. 多媒体内容占比图
plt.figure(figsize=(10, 6))
media_labels = ['有图评价', '有视频评价']
media_values = [
analysis["media_analysis"]["has_image_ratio"],
analysis["media_analysis"]["has_video_ratio"]
]
x = np.arange(len(media_labels))
plt.bar(x, media_values, width=0.5, color=['#4CAF50', '#2196F3'])
plt.xticks(x, media_labels)
plt.ylabel('占比 (%)')
plt.title('买家秀多媒体内容占比')
plt.ylim(0, 100)
for i, v in enumerate(media_values):
plt.text(i, v + 1, f'{v}%', ha='center')
plt.tight_layout()
plt.savefig(f"{output_dir}/media_ratio.png")
plt.close()
# 3. 关键词分布条形图
plt.figure(figsize=(12, 6))
keywords = analysis["keyword_analysis"]["sorted"]
if keywords:
labels = [k[0] for k in keywords]
values = [k[1] for k in keywords]
x = np.arange(len(labels))
plt.bar(x, values, color='#ff9800')
plt.xticks(x, labels)
plt.ylabel('出现次数')
plt.title('买家秀关键词分布')
plt.tight_layout()
plt.savefig(f"{output_dir}/keyword_distribution.png")
plt.close()
# 4. 热门规格分布条形图
plt.figure(figsize=(12, 6))
skus = analysis["sku_analysis"]["sorted"]
if skus:
# 处理长标签
labels = [k[0][:15] + '...' if len(k[0]) > 15 else k[0] for k in skus]
values = [k[1] for k in skus]
x = np.arange(len(labels))
plt.bar(x, values, color='#f44336')
plt.xticks(x, labels, rotation=45)
plt.ylabel('购买次数')
plt.title('热门规格购买分布')
plt.tight_layout()
plt.savefig(f"{output_dir}/sku_distribution.png")
plt.close()
logging.info(f"分析图表已保存至 {output_dir} 目录")
示例调用 if name == "main": # 替换为实际的appkey和session(从淘宝开放平台获取) APPKEY = "your_appkey" SESSION = "your_session" # 替换为目标商品ID ITEM_ID = "1234567890"
python
# 初始化API客户端
api = TaobaoItemReviewShowAPI(APPKEY, SESSION)
# 获取买家秀数据
all_reviews = api.get_all_reviews(
item_id=ITEM_ID,
max_pages=3, # 最多获取3页
sort="hot", # 按热度排序
# has_image=True # 只获取有图评价
)
if all_reviews:
# 分析买家秀
analysis = api.analyze_reviews(all_reviews)
print(f"=== 淘宝买家秀分析报告 (商品ID: {ITEM_ID}) ===")
print(f"总买家秀数量: {analysis['total_reviews']}条")
# 评分分析
print("\n评分分析:")
print(f" 平均评分: {analysis['rating_analysis']['average']}星")
print(" 评分分布:")
for rating, ratio in analysis['rating_analysis']['distribution'].items():
print(f" {rating}星: {ratio}%")
# 多媒体分析
print("\n多媒体分析:")
print(f" 有图评价占比: {analysis['media_analysis']['has_image_ratio']}%")
print(f" 有视频评价占比: {analysis['media_analysis']['has_video_ratio']}%")
print(f" 平均每条评价图片数: {analysis['media_analysis']['avg_images_per_review']}")
# 关键词分析
print("\n关键词分析:")
print(" 出现频率最高的5个关键词:")
for i, (keyword, count) in enumerate(analysis['keyword_analysis']['sorted'][:5], 1):
print(f" {i}. {keyword}: {count}次")
# 规格分析
print("\n规格分析:")
print(f" 共有{analysis['sku_analysis']['total_sku_types']}种不同规格")
if analysis['sku_analysis']['sorted']:
print(" 最受欢迎的3种规格:")
for i, (sku, count) in enumerate(analysis['sku_analysis']['sorted'][:3], 1):
print(f" {i}. {sku}: {count}人购买")
# 热门买家秀
print("\n最受欢迎的3条买家秀:")
for i, review in enumerate(analysis['top_reviews'][:3], 1):
print(f" {i}. {review['user']['nickname']} ({review['rating']}星)")
print(f" 点赞数: {review['likes']}, 评论数: {review['comments']}")
print(f" 内容: {review['content'][:100]}{'...' if len(review['content'])>100 else ''}")
print(f" 图片数: {len(review['images'])}, {'包含视频' if review['video'] else '无视频'}")
# 生成可视化图表
api.visualize_analysis(analysis)
# 示例:下载热门买家秀的图片
if not os.path.exists("review_images"):
os.makedirs("review_images")
for i, review in enumerate(analysis['top_reviews'][:3], 1):
if review['images']:
for j, img_url in enumerate(review['images'][:2], 1): # 每个买家秀下载前2张图片
api.download_image(img_url, f"review_images/top_{i}_img_{j}.jpg")
接口调用注意事项
- 调用限制与规范
- QPS 限制:淘宝开放平台对买家秀接口的 QPS 限制通常为 5-10 次 / 秒
- 权限要求:需要申请专门的权限才能访问买家秀接口,个人开发者权限有限
- 数据缓存:建议缓存获取的数据,更新周期可设置为 12-24 小时
- 图片使用:买家秀图片受版权保护,未经授权不得用于商业用途
- 调用频率:批量获取时需控制请求频率,避免触发反爬虫机制
- 常见错误及解决方案
错误码 | 说明 | 解决方案 |
---|---|---|
401 | 认证失败 | 检查 appkey 和 session 是否有效 |
403 | 权限不足 | 申请更高权限或检查接口权限配置 |
404 | 商品不存在 | 确认 item_id 是否正确 |
429 | 调用频率超限 | 降低调用频率,实现请求限流 |
500 | 服务器错误 | 实现重试机制,最多 3 次 |
110 | 参数错误 | 检查请求参数格式和取值范围 |
- 数据解析要点
- 多媒体处理:图片 URL 可能有时效性,需及时下载或处理
- 评分体系:淘宝评分通常为 1-5 星,需正确解析评分含义
- 规格信息:SKU 信息可能以编码形式返回,需映射为可读名称
- 时间格式:发布时间可能为时间戳或特定格式字符串,需统一转换
- 内容清洗:评价内容可能包含 HTML 标签和特殊字符,需清洗处理
应用场景与扩展建议
典型应用场景
- 商品优化分析:基于买家秀反馈改进产品设计和功能
- 营销素材收集:筛选优质买家秀作为官方营销素材
- 用户体验研究:分析用户真实使用场景和体验痛点
- 竞品分析系统:对比分析同类商品的买家评价和反馈
- 虚假交易检测:通过买家秀特征识别可疑交易
扩展建议
- 实现图片内容分析:使用图像识别技术分析买家秀图片中的商品状态
- 开发情感分析模块:对评价内容进行情感倾向分析,量化用户满意度
- 构建用户画像系统:基于买家秀数据构建购买用户的特征画像
- 实现自动标签生成:为买家秀自动生成标签,便于内容分类和检索
- 开发竞品对比功能:横向对比不同商品的买家秀特征和用户反馈