1. 项目概述:用Playwright监听华为商城新品预约数据
最近在帮朋友做一个手机市场趋势分析项目时,发现华为商城的新品预约数据是个很有意思的指标。作为国内头部手机厂商,华为每次新品发布前的预约量变化曲线,往往能反映出市场对产品的真实期待程度。但商城页面本身并不提供历史数据查询功能,这就需要我们自己动手抓取了。
经过技术选型对比,我最终选择了Playwright作为核心工具。相比传统的requests+BeautifulSoup组合,Playwright最大的优势在于能完美处理动态加载内容,而且自带浏览器上下文管理,特别适合这种需要模拟真实用户操作的场景。下面我就详细分享下这个项目的完整实现过程。
2. 技术选型与方案设计
2.1 为什么选择Playwright?
在初期技术调研时,我主要对比了三种方案:
-
传统requests+BeautifulSoup:虽然轻量,但无法处理JavaScript渲染的内容。华为商城的预约数据是通过接口动态加载的,直接请求页面只能拿到空壳。
-
Selenium:功能全面但启动慢,需要额外安装浏览器驱动。在需要长期运行的监听场景下,资源占用是个问题。
-
Playwright:微软开源的现代化浏览器自动化工具,支持无头模式运行,API设计更符合现代Python习惯。实测发现其执行效率比Selenium高30%左右。
最终选择Playwright的核心原因是:
- 内置Chromium/Firefox/WebKit三大引擎
- 自动等待元素加载机制完善
- 支持网络请求拦截和响应监听
- 提供丰富的页面操作API
2.2 整体架构设计
项目采用分层设计,主要模块包括:
code复制华为商城监听系统
├── 调度层 (Scheduler)
├── 采集层 (Fetcher) - Playwright实现
├── 解析层 (Parser) - XPath选择器
├── 存储层 (Storage) - SQLite+CSV
└── 可视化层 (Visualization) - Matplotlib
3. 环境准备与依赖安装
3.1 基础环境配置
推荐使用Python 3.8+环境,我这里用conda创建独立环境:
bash复制conda create -n huawei_spider python=3.8
conda activate huawei_spider
3.2 安装核心依赖
bash复制pip install playwright
playwright install # 安装浏览器二进制文件
其他辅助库:
bash复制pip install pandas matplotlib lxml
注意:Playwright首次运行时会自动下载浏览器内核(约200MB),请确保网络通畅。如果下载失败,可以手动指定镜像源:
PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright playwright install
4. 核心实现:请求与数据采集
4.1 初始化浏览器实例
python复制from playwright.sync_api import sync_playwright
def init_browser(headless=True):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=headless,
args=["--disable-blink-features=AutomationControlled"]
)
context = browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..."
)
page = context.new_page()
return page
关键参数说明:
headless=False调试时可关闭无头模式观察浏览器行为user_agent需要模拟主流浏览器的UAargs隐藏自动化特征,降低被反爬风险
4.2 监听预约数据接口
通过浏览器开发者工具分析,发现华为商城使用的是以下接口获取实时数据:
code复制https://xxx.huawei.com/api/getReservationCount?productId=12345
我们可以通过Playwright的路由拦截功能直接捕获这个请求:
python复制def setup_route(page):
def handle_route(route):
if "/api/getReservationCount" in route.request.url:
response = route.fetch()
json_data = response.json()
# 处理数据逻辑...
route.fulfill(response=response)
page.route("**/api/getReservationCount*", handle_route)
5. 数据解析与存储
5.1 解析响应数据
接口返回的JSON结构示例:
json复制{
"code": 200,
"data": {
"reservationCount": 15236,
"updateTime": "2023-11-20 14:30:00"
}
}
对应的解析代码:
python复制def parse_reservation_data(json_data):
if json_data["code"] != 200:
raise ValueError("接口响应异常")
return {
"count": json_data["data"]["reservationCount"],
"timestamp": json_data["data"]["updateTime"],
"product_id": extract_product_id_from_url(request.url)
}
5.2 数据存储方案
采用SQLite作为主存储,同时生成CSV备份:
python复制import sqlite3
from datetime import datetime
def init_db():
conn = sqlite3.connect("huawei_reservation.db")
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS reservation_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id TEXT NOT NULL,
count INTEGER NOT NULL,
timestamp DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
return conn
6. 定时调度与可视化
6.1 实现定时采集
使用APScheduler创建定时任务:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
def start_scheduler():
scheduler = BlockingScheduler()
scheduler.add_job(
fetch_data_job,
'interval',
minutes=30,
next_run_time=datetime.now()
)
scheduler.start()
6.2 数据可视化
生成预约趋势折线图:
python复制import pandas as pd
import matplotlib.pyplot as plt
def generate_trend_chart(product_id):
df = pd.read_sql(
f"SELECT timestamp, count FROM reservation_data WHERE product_id='{product_id}'",
conn
)
plt.figure(figsize=(12, 6))
plt.plot(df["timestamp"], df["count"], marker='o')
plt.title(f"华为产品 {product_id} 预约趋势")
plt.xlabel("时间")
plt.ylabel("预约量")
plt.grid()
plt.savefig(f"trend_{product_id}.png")
7. 反爬对抗实践
7.1 常见反爬措施
华为商城主要采用了以下防护手段:
- 请求频率限制(每分钟超过30次会触发验证)
- 请求头校验(缺少Referer会返回403)
- 行为特征检测(鼠标移动轨迹、点击间隔等)
7.2 应对策略
- 请求间隔随机化:
python复制import random
import time
time.sleep(random.uniform(1, 3)) # 1-3秒随机间隔
- 完整请求头模拟:
python复制headers = {
"Referer": "https://vmall.com/product/12345.html",
"Accept-Language": "zh-CN,zh;q=0.9",
"Accept-Encoding": "gzip, deflate, br"
}
- 人类行为模拟:
python复制# 模拟鼠标移动
page.mouse.move(random.randint(0, 100), random.randint(0, 100))
# 随机滚动页面
page.evaluate(f"window.scrollBy(0, {random.randint(200, 500)})")
8. 项目优化与扩展
8.1 性能优化技巧
- 浏览器实例复用:
python复制# 全局维护一个浏览器实例
_browser = None
def get_browser():
global _browser
if _browser is None:
_browser = init_browser()
return _browser
- 请求缓存机制:
python复制from diskcache import Cache
cache = Cache("request_cache")
@cache.memoize(expire=3600)
def fetch_product_page(url):
# 实际请求逻辑...
8.2 扩展方向
- 多产品并行监控:
python复制from concurrent.futures import ThreadPoolExecutor
products = ["12345", "67890", "13579"]
with ThreadPoolExecutor(max_workers=3) as executor:
executor.map(monitor_product, products)
- 异常预警系统:
python复制import smtplib
from email.mime.text import MIMEText
def send_alert_email(subject, content):
msg = MIMEText(content)
msg["Subject"] = subject
smtp.send_message(msg)
9. 常见问题排查
9.1 元素定位失败
现象:page.wait_for_selector()超时
解决方案:
- 检查元素是否在iframe中,需要先定位frame:
python复制frame = page.frame_locator("iframe#productFrame")
button = frame.locator("text=立即预约")
- 增加等待超时时间:
python复制page.wait_for_selector(".reservation-btn", timeout=10000) # 10秒
9.2 请求被拦截
现象:返回403状态码
排查步骤:
- 检查请求头是否完整
- 验证Cookie是否有效
- 降低采集频率
- 尝试更换IP地址
10. 项目总结
这个项目从技术实现角度看不算复杂,但有几个关键点值得注意:
-
动态内容处理:现代网站普遍采用前端渲染,传统爬虫方法已不适用。Playwright这类工具能完美解决这个问题。
-
反爬对抗:不能只满足于功能实现,还需要考虑长期稳定运行。请求随机化和行为模拟是必须的。
-
数据价值挖掘:单纯的采集没有意义,需要结合业务场景做数据分析。比如预约量的突变可能预示着官方即将发布重要公告。
实际运行一周后,系统稳定采集到了Mate60系列产品的完整预约曲线。通过分析发现,工作日晚8点是预约高峰期,这个洞察帮助我们优化了后续的营销活动时间安排。