一、前言
淘宝商品评论数据是电商数据分析的"金矿"——用户真实反馈、产品痛点、竞品对比都隐藏其中。但淘宝的反爬机制堪称国内电商平台最严密之一,sign签名动态加密、滑块验证、行为风控层层设防。
本文基于2024-2025年最新技术实践,详解两种主流抓取方案:PC端网页解析(适合小规模)与移动端接口逆向(适合大规模),并提供完整的Python工程代码与反爬对抗策略。
二、技术方案选型
| 方案 | 适用场景 | 技术难度 | 稳定性 | 数据完整性 |
|---|---|---|---|---|
| PC端网页解析 | 小规模测试/临时抓取 | ⭐⭐ | 中(易触发验证码) | 中等(需渲染JS) |
| 移动端API逆向 | 大规模生产环境 | ⭐⭐⭐⭐ | 高(需维护签名算法) | 高(纯JSON数据) |
| RPA自动化 | 超小规模/精准抓取 | ⭐⭐⭐ | 低(依赖页面结构) | 完整但效率低 |
推荐选择:生产环境务必采用移动端接口逆向方案,直接调用淘宝内部API,绕过前端渲染,效率提升10倍以上。
三、方案一:PC端网页解析(入门向)
3.1 接口定位与抓包
打开Chrome开发者工具(F12),进入商品详情页,滚动到评论区:
- 筛选请求:Network面板筛选
XHR/Fetch,关键词过滤rate/comment/list - 定位接口:典型URL格式:plain
https://rate.taobao.com/feedRateList.do?itemId=123456789&sellerId=987654321¤tPage=1&pageSize=20&_t=1689200000000
- 关键参数识别:
itemId:商品ID(URL中提取)sellerId:卖家ID(页面源码中查找)currentPage:分页页码_t:时间戳(防缓存)sign:动态签名(核心加密参数)
3.2 基础抓取代码
Python
import requestsimport reimport timeimport randomfrom fake_useragent import UserAgentclass TaobaoWebScraper:
def __init__(self):
self.ua = UserAgent()
self.session = requests.Session()
self.session.headers.update({
"User-Agent": self.ua.random,
"Referer": "https://item.taobao.com/",
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9",
"X-Requested-With": "XMLHttpRequest"
})
def extract_item_id(self, product_url):
"""从商品URL提取itemId"""
patterns = [
r"item\.taobao\.com/item\.htm\?.*id=(\d+)",
r"detail\.tmall\.com/item\.htm\?.*id=(\d+)",
]
for pattern in patterns:
match = re.search(pattern, product_url)
if match:
return match.group(1)
raise ValueError("无法提取商品ID")
def fetch_comments(self, item_id, page=1):
"""获取单页评论"""
api_url = "https://rate.taobao.com/feedRateList.do"
params = {
"itemId": item_id,
"currentPage": page,
"pageSize": 20,
"_t": int(time.time() * 1000), # 动态时间戳
"callback": f"jsonp_{int(time.time())}" # JSONP回调
}
try:
response = self.session.get(api_url, params=params, timeout=10)
response.raise_for_status()
# 解析JSONP格式:jsonp_xxx({...})
json_str = re.search(r'jsonp_\d+\((.*)\)', response.text)
if json_str:
import json
data = json.loads(json_str.group(1))
return self._parse_comments(data)
return None
except Exception as e:
print(f"请求失败: {e}")
return None
def _parse_comments(self, data):
"""解析评论数据结构"""
if data.get("ret", [""])[0] != "SUCCESS::调用成功":
return None
comments = []
for item in data.get("data", {}).get("comments", []):
comments.append({
"user": item.get("userNick", ""),
"content": self._clean_text(item.get("content", "")),
"date": item.get("commentTime", ""),
"star": item.get("star", 0),
"useful": item.get("useful", 0),
"images": item.get("images", [])
})
return comments
def _clean_text(self, text):
"""清洗文本(去除HTML标签和表情)"""
text = re.sub(r'<[^>]+>', '', text)
text = re.sub(r'[\U00010000-\U0010ffff]', '', text, flags=re.UNICODE)
return text.strip()# 使用示例if __name__ == "__main__":
scraper = TaobaoWebScraper()
item_id = scraper.extract_item_id("https://item.taobao.com/item.htm?id=123456789")
for page in range(1, 4):
comments = scraper.fetch_comments(item_id, page)
if comments:
print(f"第{page}页获取{len(comments)}条评论")
time.sleep(random.uniform(2, 5)) # 随机延迟防封3.3 PC端反爬对抗
淘宝PC端风控特征:
- Cookie验证:需携带有效的
cna/tracknick等Cookie - Referer校验:必须来自商品详情页
- 请求频率:单IP每秒>1次即可能触发滑块
应对策略:
- 使用
requests.Session()维持Cookie会话 - 插入随机延迟(2-5秒/请求)
- 配合代理IP池轮换(推荐付费代理如站大爷、阿布云)
四、方案二:移动端API逆向(生产级)
这是业界主流方案,通过抓包淘宝APP获取内部API,数据为纯净JSON,无需渲染,且反爬阈值更高。
4.1 抓包环境搭建
工具链:
- Charles/Fiddler:HTTPS抓包(需安装系统根证书)
- Jadx/GDA:APK反编译分析加密逻辑
- Frida:动态调试Hook加密函数(进阶)
抓包步骤:
- 手机配置Charles代理(同WiFi下)
- 淘宝APP浏览商品评论
- Charles中筛选
mtop.taobao.detail或rate相关请求 - 关键发现:淘宝使用MTOP协议(阿里巴巴移动端统一网关)
4.2 MTOP协议核心机制
移动端接口典型特征:
http
GET https://h5api.m.taobao.com/h5/mtop.taobao.rate.detaillist.get/6.0/
Headers:
x-m-appkey: 12574478
x-m-timestamp: 1700000000000
x-m-sign: xxxxxxxxxxxxxxxx ← 核心加密参数
x-m-c-trace-id: xxx
Cookie: _m_h5_tk=xxxxx; _m_h5_tk_enc=xxxxx签名生成逻辑(通过Jadx逆向得出):
java
// 伪代码:淘宝APP内的签名算法String sign = MD5(
_m_h5_tk.split("_")[0] + // Cookie中的token前半段
"&" +
timestamp +
"&" +
appKey +
"&" +
requestData // 业务参数JSON字符串);4.3 完整逆向实现代码
Python
import requestsimport hashlibimport timeimport jsonimport refrom urllib.parse import quoteclass TaobaoMobileAPI:
def __init__(self):
self.app_key = "12574478" # 淘宝H5固定appKey
self.api_base = "https://h5api.m.taobao.com/h5"
self.session = requests.Session()
# 初始化时需获取_m_h5_tk(通过首页请求或扫码登录)
self.m_h5_tk = None
self._init_token()
def _init_token(self):
"""初始化获取_m_h5_tk(简化版:通过访问首页获取)"""
# 实际生产环境需模拟登录或使用扫码获取有效Cookie
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15",
"Referer": "https://h5.m.taobao.com/"
}
# 此处应实现登录逻辑,示例使用预设Cookie
self.session.headers.update(headers)
self.session.cookies.update({
"_m_h5_tk": "your_token_here_xxx",
"_m_h5_tk_enc": "your_enc_token_here"
})
self.m_h5_tk = self.session.cookies.get("_m_h5_tk", "")
def _generate_sign(self, api_name, data, t=None):
"""
生成MTOP签名(核心算法)
算法:md5(token&t&appKey&data)
"""
if not t:
t = int(time.time() * 1000)
# 提取token前半段(_m_h5_tk格式:token_timestamp)
token = self.m_h5_tk.split("_")[0] if self.m_h5_tk else ""
# 拼接待加密字符串
params_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
sign_str = f"{token}&{t}&{self.app_key}&{params_str}"
# MD5加密
sign = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
return sign, t, params_str
def fetch_comments(self, item_id, page=1, page_size=20):
"""调用MTOP接口获取评论"""
api_name = "mtop.taobao.rate.detaillist.get"
api_version = "6.0"
# 业务参数
data = {
"itemId": str(item_id),
"pageNum": page,
"pageSize": page_size,
"sort": "default", # default/time/score
"hasPic": False,
"fold": 0
}
# 生成签名
sign, t, data_str = self._generate_sign(api_name, data)
# 构造请求
url = f"{self.api_base}/{api_name}/{api_version}/"
params = {
"jsv": "2.7.2",
"appKey": self.app_key,
"t": t,
"sign": sign,
"api": api_name,
"v": api_version,
"type": "jsonp",
"dataType": "jsonp",
"callback": f"mtopjsonp{int(time.time())}",
"data": data_str }
try:
response = self.session.get(url, params=params, timeout=15)
# 解析JSONP
json_match = re.search(r'mtopjsonp\d+\((.*)\)', response.text)
if not json_match:
# 检查是否触发反爬
if "FAIL_SYS_TOKEN_EXOIRED" in response.text:
print("Token过期,需重新获取")
self._init_token()
return None
return None
result = json.loads(json_match.group(1))
if result.get("ret", [""])[0] == "SUCCESS::调用成功":
return result.get("data", {}).get("comments", [])
else:
print(f"API错误: {result.get('ret')}")
return None
except Exception as e:
print(f"请求异常: {e}")
return None
def crawl_all_comments(self, item_id, max_pages=10):
"""全量抓取评论"""
all_comments = []
for page in range(1, max_pages + 1):
print(f"抓取第 {page}/{max_pages} 页...")
comments = self.fetch_comments(item_id, page)
if not comments:
break
all_comments.extend(comments)
# 检查是否最后一页
if len(comments) < 20:
break
# 随机延迟(移动端阈值较高,但仍需控制频率)
time.sleep(random.uniform(0.5, 1.5))
return all_comments# 生产环境建议:封装为服务并添加代理池class ProductionCrawler(TaobaoMobileAPI):
def __init__(self, proxy_pool=None):
super().__init__()
self.proxy_pool = proxy_pool or []
self.current_proxy = None
def _get_proxy(self):
"""轮询获取代理"""
if not self.proxy_pool:
return None
self.current_proxy = random.choice(self.proxy_pool)
return {
"http": f"http://{self.current_proxy}",
"https": f"http://{self.current_proxy}"
}
def fetch_comments(self, item_id, page=1, page_size=20):
"""带代理和重试的请求"""
max_retries = 3
for i in range(max_retries):
try:
proxy = self._get_proxy()
# 在父类请求基础上添加代理
# ...(代码省略,实际需重写请求逻辑添加proxies参数)
return super().fetch_comments(item_id, page, page_size)
except Exception as e:
if i == max_retries - 1:
raise
time.sleep(2 ** i) # 指数退避4.4 关键逆向技巧
1. Token维护机制:
_m_h5_tk有效期约24小时,需定时刷新- 通过访问
https://h5.m.taobao.com/首页自动更新Cookie
2. 签名算法追踪:
- 若淘宝升级加密(如改为HMAC-SHA256),需重新反编译APK
- 使用Frida Hook
com.taobao.wireless.security.adapter.JNICLibrary.doCommandNative函数动态获取签名
3. 设备指纹模拟:
Python
# 模拟淘宝APP设备指纹headers = {
"x-umidtoken": "Txxx_xxx", # 通过算法生成或从真机提取
"x-wap-profile": "https://img.taobao.com/...",
"x-m-biz-code": "taobao",
"x-m-biz-data": "..." # 设备环境加密数据}五、反爬对抗实战策略
5.1 风控触发场景与应对
| 风控等级 | 触发特征 | 应对策略 |
|---|---|---|
| 初级 | 要求滑动验证码 | 接入打码平台(如超级鹰)或使用Selenium过滑块 |
| 中级 | 返回"访问频繁"或空数据 | 切换代理IP + 增加请求间隔(>5秒) |
| 高级 | 账号封禁/设备拉黑 | 更换设备指纹 + 使用新账号 + 降低频率 |
5.2 代理IP池架构
Python
import redisfrom concurrent.futures import ThreadPoolExecutorclass ProxyManager:
def __init__(self):
self.redis_client = redis.Redis(host='localhost', port=6379, db=0)
self.proxy_key = "taobao:proxies"
def validate_proxy(self, proxy):
"""验证代理可用性(访问淘宝测试接口)"""
test_url = "https://h5api.m.taobao.com/h5/mtop.taobao.wireless.home.load/1.0/"
try:
resp = requests.get(test_url, proxy={"https": proxy}, timeout=5)
return resp.status_code == 200
except:
return False
def get_valid_proxy(self):
"""从Redis获取可用代理"""
proxy = self.redis_client.srandmember(self.proxy_key)
return proxy.decode() if proxy else None
def feedback_proxy(self, proxy, success):
"""代理质量反馈(失败次数过多则移除)"""
if not success:
self.redis_client.hincrby("taobao:proxy_failures", proxy, 1)
if self.redis_client.hget("taobao:proxy_failures", proxy) > b"5":
self.redis_client.srem(self.proxy_key, proxy)5.3 数据存储与监控
Python
from sqlalchemy import create_engine, Column, String, Integer, Text, DateTimefrom sqlalchemy.orm import sessionmakerimport pandas as pdclass CommentStorage:
def __init__(self, db_url="sqlite:///taobao_comments.db"):
self.engine = create_engine(db_url)
self.Session = sessionmaker(bind=self.engine)
self._init_tables()
def save_comments(self, item_id, comments):
"""批量保存评论"""
session = self.Session()
try:
for c in comments:
session.add(CommentModel(
item_id=item_id,
user_nick=c['user'],
content=c['content'],
star=c['star'],
comment_time=c['date'],
raw_data=json.dumps(c)
))
session.commit()
except Exception as e:
session.rollback()
raise e finally:
session.close()
def export_to_excel(self, item_id, filepath):
"""导出分析报表"""
session = self.Session()
comments = session.query(CommentModel).filter_by(item_id=item_id).all()
df = pd.DataFrame([c.to_dict() for c in comments])
# 简单情感分析
df['sentiment'] = df['content'].apply(self._simple_sentiment)
df.to_excel(filepath, index=False)
def _simple_sentiment(self, text):
"""基于关键词的简单情感判断"""
positive_words = ['好评', '满意', '喜欢', '推荐', '不错']
negative_words = ['差评', '失望', '垃圾', '假货', '退货']
score = sum(1 for w in positive_words if w in text) - \ sum(1 for w in negative_words if w in text)
return "positive" if score > 0 else "negative" if score < 0 else "neutral"六、合规与法律边界
⚠️ 重要声明:以下行为可能违反《淘宝平台服务协议》及《网络安全法》:
- 禁止行为:
- 破解淘宝加密算法用于商业竞品分析
- 高频抓取导致淘宝服务器负载异常
- 抓取用户隐私信息(如匿名买家真实身份)
- 将数据用于刷单、炒信等违规场景
- 合规建议:
- 仅采集公开可见的评论内容
- 请求频率控制在单IP每秒<1次,日请求<1000次
- 数据仅用于个人学习、学术研究或内部产品优化
- 考虑接入淘宝官方开放平台API(需申请权限)
七、总结与进阶
本文详解了淘宝评论抓取的两大技术路径:
- PC端方案:适合快速验证,但受限于反爬,仅适合小规模
- 移动端逆向:生产环境首选,需持续维护签名算法,配合代理池可实现日采10万+评论
进阶方向:
- 实时采集:基于WebSocket监听淘宝直播评论(需逆向直播API)
- 图文分析:使用OCR提取评论图片中的文字(如快递单、产品瑕疵图)
- 知识图谱:构建用户-商品-评论关系图,挖掘水军账号
如遇任何疑问或有进一步的需求,请随时与我私信或者评论联系。