基于Scrapy的天猫商品数据爬取与分析实战(含API签名破解与可视化)
本文以华为Mate 60 Pro为例,详细介绍如何使用Scrapy框架爬取天猫商品数据,涵盖API签名破解、反爬应对、数据存储及可视化全流程,适合爬虫进阶学习者实践。
一、抓包分析:定位天猫商品API
1.1 目标与工具
- 目标:获取华为Mate 60 Pro的价格、销量等数据
- 工具:Chrome开发者工具(F12)、Mitmproxy(可选)
1.2 操作步骤
-
登录天猫并打开商品页
访问https://detail.tmall.com/item.htm?id=725643567890
,右键点击页面 → 检查,切换到 Network 面板。 -
刷新页面抓包
输入关键词detail/get.json
过滤请求,找到目标API:https://api.tmall.com/rest/item/1.0/item/detail/get.json?itemId=725643567890&t=1685275400000&sign=abc123...
-
提取关键参数
itemId
:商品ID(725643567890
)t
:13位时间戳(如1685275400000
)sign
:MD5签名(需逆向生成)
二、环境搭建:Scrapy项目初始化
2.1 创建虚拟环境与依赖安装
# 创建虚拟环境
python -m venv venv
# 激活环境(Windows)
venv\Scripts\activate.bat
# 安装依赖
pip install scrapy requests cryptography matplotlib
2.2 初始化Scrapy项目
scrapy startproject tmall_huawei
cd tmall_huawei
scrapy genspider huawei_spider tmall.com
2.3 项目结构
tmall_huawei/
├── scrapy.cfg
└── tmall_huawei/
├── items.py # 数据结构定义
├── middlewares.py # 反爬中间件
├── pipelines.py # 数据存储
├── settings.py # 配置文件
└── spiders/
└── huawei_spider.py # 爬虫逻辑
└── utils/
└── crypto.py # 签名生成函数
三、核心开发:签名生成与爬虫逻辑
3.1 编写签名生成函数(utils/crypto.py
)
import hashlib
import time
def generate_tmall_sign(item_id, app_key="12574478", salt="0c8a5244c7d2b6e1b"):
"""生成天猫API签名"""
t = str(int(time.time() * 1000)) # 13位时间戳
sign_str = f"{t}{item_id}{app_key}{salt}" # 拼接规则需与服务端一致
sign = hashlib.md5(sign_str.encode()).hexdigest().lower() # 转小写
return {"t": t, "sign": sign, "appKey": app_key}
# 测试函数
if __name__ == "__main__":
params = generate_tmall_sign("725643567890")
print(f"生成的时间戳:{params['t']},签名:{params['sign']}")
3.2 定义数据结构(items.py
)
import scrapy
class TmallHuaweiItem(scrapy.Item):
item_id = scrapy.Field() # 商品ID
title = scrapy.Field() # 商品标题
price = scrapy.Field() # 价格
sales = scrapy.Field() # 月销量
shop_name = scrapy.Field() # 店铺名称
timestamp = scrapy.Field() # 采集时间
3.3 编写爬虫逻辑(spiders/huawei_spider.py
)
import scrapy
from urllib.parse import urlencode
from ..utils.crypto import generate_tmall_sign
from ..items import TmallHuaweiItem
class HuaweiSpiderSpider(scrapy.Spider):
name = "huawei_spider"
allowed_domains = ["tmall.com"]
start_urls = ["https://detail.tmall.com/item.htm?id=725643567890"]
def start_requests(self):
item_id = "725643567890"
sign_params = generate_tmall_sign(item_id)
# 构造API参数
params = {
"itemId": item_id,
"type": "json",
"version": "1.0",
"isLowPrice": "false",
**sign_params
}
api_url = f"https://api.tmall.com/rest/item/1.0/item/detail/get.json?{urlencode(params)}"
# 发送带请求头的API请求
yield scrapy.Request(
api_url,
callback=self.parse_item,
headers=self.get_headers(),
meta={"item_id": item_id}
)
def parse_item(self, response):
item = TmallHuaweiItem()
data = response.json()
item_info = data.get("data", {}).get("item", {})
shop_info = item_info.get("shop", {})
item["item_id"] = response.meta["item_id"]
item["title"] = item_info.get("title", "")
item["price"] = item_info.get("sellPrice", {}).get("price", "0.0")
item["sales"] = item_info.get("sales", "0")
item["shop_name"] = shop_info.get("name", "")
item["timestamp"] = int(time.time())
yield item
def get_headers(self):
"""模拟浏览器请求头(含Referer和User-Agent)"""
return {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/114.0.0.0 Safari/537.36",
"Referer": "https://detail.tmall.com/item.htm?id=725643567890",
"Accept": "application/json, text/plain, */*"
}
四、数据存储与可视化
4.1 存储到CSV(pipelines.py
)
import csv
import time
class CSVPipeline:
def __init__(self):
self.filename = f"huawei_mate60_{time.strftime('%Y%m%d_%H%M')}.csv"
self.file = open(self.filename, "w", newline="utf-8", encoding="utf-8-sig") # 防止中文乱码
self.writer = csv.DictWriter(self.file, fieldnames=["item_id", "title", "price", "sales", "shop_name", "timestamp"])
self.writer.writeheader()
def process_item(self, item, spider):
self.writer.writerow(dict(item))
return item
def close_spider(self, spider):
self.file.close()
4.2 启用管道(settings.py
)
ITEM_PIPELINES = {
"tmall_huawei.pipelines.CSVPipeline": 300,
}
4.3 价格趋势可视化(visualize.py
)
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
def plot_price_trend(csv_path):
df = pd.read_csv(csv_path)
df["price"] = df["price"].astype(float)
df["time"] = df["timestamp"].apply(lambda x: datetime.fromtimestamp(x))
plt.figure(figsize=(12, 6))
plt.plot(df["time"], df["price"], marker="o", color="#FF6B6B", linestyle="-")
plt.title("华为Mate 60 Pro价格趋势", fontsize=16)
plt.xlabel("时间", fontsize=12)
plt.ylabel("价格(元)", fontsize=12)
plt.xticks(rotation=45)
plt.grid(True, linestyle="--", alpha=0.7)
plt.tight_layout()
plt.show()
if __name__ == "__main__":
plot_price_trend("huawei_mate60_20250527_1530.csv")
五、调试技巧与反爬应对
5.1 常见错误与解决
错误类型 | 原因分析 | 解决方法 |
---|---|---|
403 Forbidden | 签名错误或缺少请求头 | 对比抓包签名,添加Cookie和Referer |
JSONDecodeError | API返回非JSON数据 | 检查URL是否正确,确保登录态 |
KeyError: 'data' | 响应结构变化 | 重新抓包分析JSON路径 |
5.2 反爬策略
- 请求间隔:在
settings.py
中设置DOWNLOAD_DELAY = 2
- User-Agent轮换:使用
fake_useragent
库动态切换UA - 代理IP池:集成
scrapy-proxies
中间件(需准备代理服务)
六、总结
本文通过实战演示了天猫商品数据爬取的完整流程,核心技术点包括:
- 通过Chrome抓包定位API及参数逆向
- 使用Scrapy框架实现分布式爬虫
- MD5签名生成与反爬应对
- 数据存储与可视化分析
实际应用中需根据网站反爬机制动态调整策略(如动态盐值、验证码处理),进一步可扩展为分布式集群或集成监控告警系统。