1. 项目背景与核心挑战
最近在做一个知识付费平台的数据分析项目,需要爬取得到App"知识城邦"板块的热门圈子动态。这个需求看似简单,但实际操作中遇到了几个硬骨头:
- 得到App采用了混合渲染方案,核心数据通过接口动态加载
- 关键接口都有签名验证,直接抓包逆向成本太高
- 页面元素结构复杂,传统爬虫工具难以稳定定位
- 需要模拟完整用户行为流才能触发数据加载
经过多轮技术选型,最终确定使用Playwright作为核心解决方案。这个微软开源的浏览器自动化工具,在移动端H5页面抓取方面展现出独特优势。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与环境搭建
2.1 为什么选择Playwright?
相比传统的Selenium或Requests方案,Playwright有三大杀手锏:
- 全浏览器支持:Chromium、WebKit、Firefox三引擎支持,完美适配各种H5页面
- 移动端模拟:自带设备模拟功能,可以完美伪装成手机浏览器
- 自动等待机制:智能等待元素加载,避免手动写sleep的尴尬
安装只需要一行命令:
bash复制pip install playwright && playwright install
2.2 设备模拟配置
要伪装成真实的手机访问,需要配置设备参数:
python复制from playwright.sync_api import sync_playwright
device = {
'user_agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15',
'viewport': {'width': 375, 'height': 812},
'device_scale_factor': 3,
'is_mobile': True
}
3. 核心爬取逻辑实现
3.1 登录态保持方案
得到App的登录态主要通过Cookie维持,这里采用手动登录+持久化上下文的方式:
python复制with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context(**device)
page = context.new_page()
# 手动登录后保存状态
page.goto('https://www.dedao.cn/login')
input("请手动登录后按回车继续...")
context.storage_state(path='auth.json')
3.2 动态内容抓取技巧
知识城邦的动态加载采用滚动触发的分页模式,需要模拟完整用户行为:
python复制def scroll_to_bottom(page):
prev_height = 0
while True:
curr_height = page.evaluate('document.body.scrollHeight')
if curr_height == prev_height:
break
page.mouse.wheel(0, curr_height)
prev_height = curr_height
page.wait_for_timeout(2000)
3.3 数据解析优化
动态内容的DOM结构复杂,建议使用Playwright的定位器API:
python复制posts = page.locator('css=.post-item')
for i in range(posts.count()):
post = posts.nth(i)
title = post.locator('css=.title').inner_text()
author = post.locator('css=.author').get_attribute('data-id')
print(f'{i+1}. {title} - {author}')
4. 反反爬策略实战
4.1 行为指纹伪装
通过随机化操作间隔和轨迹来模拟真人行为:
python复制import random
from time import sleep
def human_like_click(element):
box = element.bounding_box()
x = box['x'] + box['width'] * random.uniform(0.2, 0.8)
y = box['y'] + box['height'] * random.uniform(0.2, 0.8)
page.mouse.move(x, y)
sleep(random.uniform(0.5, 1.5))
element.click()
4.2 请求限流控制
建议使用令牌桶算法控制请求频率:
python复制from threading import Semaphore
class RequestLimiter:
def __init__(self, rate=5):
self.sem = Semaphore(rate)
def acquire(self):
self.sem.acquire()
def release(self):
sleep(1) # 每秒最多5次
self.sem.release()
5. 数据存储与清洗
5.1 结构化存储方案
推荐使用MongoDB存储非结构化数据:
python复制from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
db = client['dedao']
collection = db['posts']
def save_to_mongo(data):
try:
collection.update_one(
{'post_id': data['post_id']},
{'$set': data},
upsert=True
)
except Exception as e:
print(f'存储失败: {e}')
5.2 数据清洗技巧
处理富文本内容的实用函数:
python复制import re
from html import unescape
def clean_html(text):
text = unescape(text) # 转换HTML实体
text = re.sub(r'<[^>]+>', '', text) # 去除HTML标签
text = re.sub(r'\s+', ' ', text) # 合并空白字符
return text.strip()
6. 项目部署建议
6.1 分布式爬虫架构
对于大规模抓取,可以采用Scrapy+Playwright的组合:
python复制class DedaoSpider(scrapy.Spider):
name = 'dedao'
def start_requests(self):
yield scrapy.Request(
url='https://m.dedao.cn',
meta={'playwright': True}
)
6.2 定时任务管理
使用APScheduler实现定时抓取:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
@sched.scheduled_job('cron', hour=3)
def scheduled_job():
run_spider()
sched.start()
7. 踩坑经验分享
- 元素定位失效:得到App经常变更CSS类名,建议使用XPath结合语义化属性定位
- 请求被拦截:遇到403时可以尝试禁用JavaScript再重试
- 滑动验证码:出现验证码时需要切换IP或使用打码平台
- 内存泄漏:长时间运行后Playwright会占用大量内存,建议定期重启浏览器实例
重要提示:商业用途抓取前请务必评估法律风险,建议控制请求频率在合理范围内
