在电商数据分析领域,商品评价数据是了解用户需求、优化产品体验的重要依据。本文将记录笔者开发淘宝商品评价实时采集系统的全过程,从 API 文档分析到完整代码实现,为开发者提供可参考的实战经验。
1. API 文档分析与需求理解
淘宝平台提供了多个与商品评价相关的 API 接口,经过详细比对,最终选择接口作为数据采集入口。该接口的主要特点:
-
功能:获取淘宝客商品的评价列表
-
请求方式:HTTP POST
-
参数要求:
num_iid
:商品 ID(必传)fields
:返回字段列表(必传)page_no
:页码page_size
:每页数量(最大 40)
-
返回格式:JSON
-
权限要求:需申请商品评价读取权限
-
调用频率限制:10 次 / 秒(根据应用等级可能不同)
2. 开发环境搭建
技术栈选择:
- 编程语言:Python 3.9
- 主要依赖库:
requests==2.28.1 # HTTP请求
pandas==1.5.3 # 数据处理
pymysql==1.0.3 # MySQL连接
python-dotenv==1.0.0 # 环境变量管理
apscheduler==3.10.1 # 定时任务
项目结构:
taobao-comment-crawler/
├── config/
│ └── .env # 配置文件
├── utils/
│ ├── api_client.py # API调用工具
│ └── db_utils.py # 数据库工具
├── models/
│ └── comment.py # 数据模型
├── tasks/
│ └── comment_task.py # 定时任务
└── main.py # 入口文件
3. 认证授权实现
淘宝 API 采用 OAuth2.0 授权码模式,需获取 Access Token 才能调用接口:
# utils/api_client.py
import os
import requests
import json
import time
from urllib.parse import urlencode
from dotenv import load_dotenv
load_dotenv()
class TaobaoClient:
def __init__(self):
self.app_key = os.getenv("APP_KEY")
self.app_secret = os.getenv("APP_SECRET")
self.redirect_uri = os.getenv("REDIRECT_URI")
self.token_file = os.getenv("TOKEN_FILE", "taobao_token.json")
self.api_url = "https://eco.taobao.com/router/rest"
def get_authorization_url(self):
"""生成授权URL"""
params = {
"client_id": self.app_key,
"redirect_uri": self.redirect_uri,
"response_type": "code",
"state": "init"
}
return f"https://oauth.taobao.com/authorize?{urlencode(params)}"
def get_access_token(self, auth_code):
"""通过授权码获取Access Token"""
url = "https://oauth.taobao.com/token"
payload = {
"grant_type": "authorization_code",
"client_id": self.app_key,
"client_secret": self.app_secret,
"code": auth_code,
"redirect_uri": self.redirect_uri
}
response = requests.post(url, data=payload)
token_data = response.json()
# 保存Token到文件
with open(self.token_file, "w") as f:
json.dump(token_data, f)
return token_data
def refresh_access_token(self):
"""刷新Access Token"""
try:
with open(self.token_file, "r") as f:
token_data = json.load(f)
url = "https://oauth.taobao.com/token"
payload = {
"grant_type": "refresh_token",
"client_id": self.app_key,
"client_secret": self.app_secret,
"refresh_token": token_data["refresh_token"]
}
response = requests.post(url, data=payload)
new_token_data = response.json()
# 更新Token文件
with open(self.token_file, "w") as f:
json.dump(new_token_data, f)
return new_token_data
except Exception as e:
print(f"刷新Token失败: {str(e)}")
return None
def get_current_token(self):
"""获取当前有效的Access Token"""
try:
with open(self.token_file, "r") as f:
token_data = json.load(f)
# 检查Token是否过期(提前10分钟刷新)
if time.time() > token_data.get("expires_in", 0) - 600:
return self.refresh_access_token()
return token_data
except (FileNotFoundError, json.JSONDecodeError):
print("请先完成授权流程获取Access Token")
return None
4. API 请求签名实现
淘宝 API 要求所有请求参数必须经过 MD5 签名:
# utils/api_client.py (续)
import hashlib
class TaobaoClient:
# ... 已有代码 ...
def generate_sign(self, params):
"""生成API请求签名"""
# 按参数名排序
sorted_params = sorted(params.items(), key=lambda x: x[0])
# 拼接参数名和值
string_to_sign = self.app_secret
for key, value in sorted_params:
string_to_sign += f"{key}{value}"
string_to_sign += self.app_secret
# MD5加密
signature = hashlib.md5(string_to_sign.encode("utf-8")).hexdigest().upper()
return signature
def call_api(self, method, params):
"""调用淘宝API"""
token_data = self.get_current_token()
if not token_data:
return None
# 构造公共参数
common_params = {
"method": method,
"app_key": self.app_key,
"session": token_data["access_token"],
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"format": "json",
"v": "2.0",
"sign_method": "md5"
}
# 合并参数
all_params = {**common_params, **params}
# 生成签名
all_params["sign"] = self.generate_sign(all_params)
# 发送请求
response = requests.post(self.api_url, data=all_params)
if response.status_code == 200:
return response.json()
else:
print(f"请求失败: {response.status_code} - {response.text}")
return None
5. 商品评价采集实现
# tasks/comment_task.py
import time
from utils.api_client import TaobaoClient
from models.comment import CommentModel
class CommentCollector:
def __init__(self):
self.client = TaobaoClient()
self.model = CommentModel()
self.page_size = 20 # 每页20条评价
def collect_comments(self, item_id, max_pages=10):
"""采集商品评价"""
print(f"开始采集商品 {item_id} 的评价...")
all_comments = []
for page in range(1, max_pages + 1):
print(f"正在采集第 {page}/{max_pages} 页评价...")
# 调用API获取评价
params = {
"fields": "comment_id,item_id,content,rate,created,useful",
"num_iid": item_id,
"page_no": page,
"page_size": self.page_size
}
result = self.client.call_api("taobao.taobaoke.items.comments.get", params)
if not result:
print("获取评价失败,跳过当前页")
continue
# 解析评价数据
comments = self._parse_comments(result, item_id)
if not comments:
print("没有更多评价,结束采集")
break
all_comments.extend(comments)
# 保存到数据库
self.model.save_comments(comments)
# 检查是否有下一页
if len(comments) < self.page_size:
print("已获取全部评价,结束采集")
break
# 控制采集频率,避免触发限流
time.sleep(1)
print(f"商品 {item_id} 评价采集完成,共获取 {len(all_comments)} 条评价")
return all_comments
def _parse_comments(self, api_response, item_id):
"""解析API返回的评价数据"""
if not api_response:
return []
# 检查是否有错误
if "error_response" in api_response:
error = api_response["error_response"]
print(f"API错误: {error.get('code')} - {error.get('msg')}")
return []
# 提取评价列表
comments = api_response.get("taobaoke_items_comments_get_response", {}) \
.get("comments", {}) \
.get("n_tbk_item_comment", [])
parsed_comments = []
for comment in comments:
parsed = {
"comment_id": comment.get("comment_id"),
"item_id": item_id, # 确保item_id正确
"content": comment.get("content"),
"rate": int(comment.get("rate", 5)),
"created": comment.get("created"),
"useful": int(comment.get("useful", 0)),
"crawl_time": time.strftime("%Y-%m-%d %H:%M:%S")
}
parsed_comments.append(parsed)
return parsed_comments
6. 数据模型与存储
# models/comment.py
import pymysql
import os
from dotenv import load_dotenv
load_dotenv()
class CommentModel:
def __init__(self):
self.db_config = {
"host": os.getenv("DB_HOST", "localhost"),
"user": os.getenv("DB_USER"),
"password": os.getenv("DB_PASSWORD"),
"database": os.getenv("DB_NAME"),
"charset": "utf8mb4",
"cursorclass": pymysql.cursors.DictCursor
}
def save_comments(self, comments):
"""保存评价数据到数据库"""
if not comments:
return
try:
with pymysql.connect(**self.db_config) as conn:
with conn.cursor() as cursor:
# 插入评价数据
sql = """
INSERT INTO taobao_comments
(comment_id, item_id, content, rate, created, useful, crawl_time)
VALUES (%s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
content=VALUES(content), rate=VALUES(rate),
useful=VALUES(useful), crawl_time=VALUES(crawl_time)
"""
data = [
(
comment["comment_id"],
comment["item_id"],
comment["content"],
comment["rate"],
comment["created"],
comment["useful"],
comment["crawl_time"]
)
for comment in comments
]
cursor.executemany(sql, data)
conn.commit()
print(f"成功保存 {len(comments)} 条评价数据")
except Exception as e:
print(f"保存评价失败: {str(e)}")
7. 定时任务配置
# main.py
from apscheduler.schedulers.blocking import BlockingScheduler
from tasks.comment_task import CommentCollector
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
def scheduled_comment_collection():
"""定时采集商品评价"""
logger = logging.getLogger("CommentCollector")
logger.info("开始定时采集商品评价...")
collector = CommentCollector()
# 从配置文件或数据库获取需要监控的商品列表
items_to_monitor = [
"687674347856", # 示例商品ID,替换为实际商品ID
"687674347857",
"687674347858"
]
# 遍历商品列表,采集评价
for item_id in items_to_monitor:
try:
logger.info(f"开始采集商品 {item_id} 的评价...")
collector.collect_comments(item_id, max_pages=5)
# 商品间间隔,避免触发限流
time.sleep(5)
except Exception as e:
logger.error(f"采集商品 {item_id} 评价失败: {str(e)}")
logger.info("定时采集任务完成")
if __name__ == "__main__":
# 设置定时任务(每天凌晨3点执行)
scheduler = BlockingScheduler()
scheduler.add_job(scheduled_comment_collection, 'cron', hour='3')
print("定时任务已启动,按Ctrl+C退出")
scheduler.start()
8. 数据库设计
-- 创建数据库
CREATE DATABASE IF NOT EXISTS taobao_data DEFAULT CHARACTER SET utf8mb4;
-- 使用数据库
USE taobao_data;
-- 创建商品评价表
CREATE TABLE IF NOT EXISTS taobao_comments (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
comment_id VARCHAR(50) NOT NULL COMMENT '评价ID',
item_id VARCHAR(50) NOT NULL COMMENT '商品ID',
content TEXT COMMENT '评价内容',
rate TINYINT DEFAULT 5 COMMENT '评分(1-5)',
created DATETIME COMMENT '评价时间',
useful INT DEFAULT 0 COMMENT '有用数',
crawl_time DATETIME COMMENT '采集时间',
UNIQUE KEY idx_comment_id (comment_id),
INDEX idx_item_id (item_id),
INDEX idx_created (created),
INDEX idx_crawl_time (crawl_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='淘宝商品评价表';
9. 开发过程中的挑战与解决方案
-
API 权限申请困难
- 挑战:淘宝开放平台对商品评价 API 的权限审核严格
- 解决方案:提交详细的应用使用说明,强调数据仅用于分析和研究
-
签名算法调试耗时
- 挑战:API 签名错误导致请求失败
- 解决方案:使用日志详细记录参数拼接过程,对比官方示例逐步排查
-
数据处理效率问题
- 挑战:大量评价数据处理缓慢
- 解决方案:使用
executemany
批量插入数据库,优化 SQL 语句
-
API 限流频繁
- 挑战:频繁请求导致 IP 被封禁
- 解决方案:添加请求间隔控制,实现指数退避重试机制
10. 部署与运维
- 环境变量配置(.env 文件)
APP_KEY=你的AppKey
APP_SECRET=你的AppSecret
REDIRECT_URI=你的回调URL
TOKEN_FILE=taobao_token.json
DB_HOST=localhost
DB_USER=你的数据库用户名
DB_PASSWORD=你的数据库密码
DB_NAME=taobao_data
2.部署步骤
# 克隆项目
git clone https://github.com/your-repo/taobao-comment-crawler.git
cd taobao-comment-crawler
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Windows使用: venv\Scripts\activate
# 安装依赖
pip install -r requirements.txt
# 配置环境变量
cp .env.example .env
# 编辑.env文件,填入实际配置信息
# 初始化数据库
mysql -u username -p < database/schema.sql
# 首次运行需要授权
python main.py --authorize
# 启动定时任务
python main.py
总结
通过本次开发实录,我们完成了从 API 文档分析到完整运行的淘宝商品评价实时采集系统。核心技术要点包括:
- OAuth2.0 授权流程实现
- API 请求签名算法
- 分页数据采集与处理
- 数据库设计与优化
- 定时任务调度
在实际应用中,还可以根据业务需求扩展更多功能,如情感分析、关键词提取、评价趋势图表等。通过合理的架构设计和性能优化,可以构建出稳定、高效的电商数据采集与分析平台。