markdown复制## 1. 项目背景与核心挑战
最近在做一个本地生活服务的数据分析项目,需要获取某点评网站的商户数据。本以为是个简单的爬虫任务,实际动手才发现这个网站的页面全是动态渲染的,传统requests+BeautifulSoup组合完全失效。页面数据通过XHR异步加载,关键内容都被加密在JavaScript bundles里,连最简单的店铺名称都拿不到。
经过两周的实战摸索,最终用Selenium+Playwright双引擎方案攻克了这个动态页面爬取难题。这个方案最大的优势在于:
- 能100%模拟人类操作触发数据加载
- 支持自动等待AJAX请求完成
- 可处理各种反爬机制(滑块验证、点击验证等)
- 双引擎互为备份确保稳定性
> 重要提示:实际爬取请遵守网站robots.txt规则,控制请求频率,本文仅讨论技术实现方案
## 2. 技术选型与方案设计
### 2.1 为什么需要双引擎?
单纯使用Selenium会遇到几个致命问题:
1. 页面加载速度慢(特别是Headless模式)
2. 内存泄漏导致长时间运行崩溃
3. 某些动态元素无法定位
Playwright的加入完美解决了这些问题:
- 原生支持无头模式且速度更快
- 自动管理浏览器上下文
- 更强大的选择器引擎
### 2.2 技术栈组成
```python
核心组件:
- Selenium 4.0+(WebDriver管理)
- Playwright 1.30+(备用渲染引擎)
- undetected-chromedriver(绕过Cloudflare检测)
- BrowserMob Proxy(流量监控)
- Redis(任务队列去重)
3. 核心实现细节
3.1 动态加载触发机制
目标网站的列表页采用无限滚动加载,需要模拟滚动到底部的行为。这里有个关键技巧:不能用简单的window.scrollTo(),必须加入随机停顿和曲线移动。
python复制def smooth_scroll(driver, scroll_times=3):
for _ in range(scroll_times):
# 生成抛物线滚动轨迹
scroll_script = """
let height = document.body.scrollHeight;
let current = 0;
let speed = 20;
function scroll() {
current += speed;
speed *= 0.95;
window.scrollTo(0, current);
if(current < height) {
requestAnimationFrame(scroll);
}
}
scroll();
"""
driver.execute_script(scroll_script)
time.sleep(random.uniform(1.5, 3.5))
3.2 数据提取的三种策略
根据页面结构特点,我们组合使用了三种提取方式:
-
DOM解析:常规XPath定位
python复制shop_name = driver.find_element( By.XPATH, '//div[@class="shop-name"]/text()' ).get_attribute('innerHTML') -
网络请求拦截:捕获XHR响应
python复制async def handle_response(response): if '/api/shopinfo' in response.url: data = await response.json() save_to_db(data) page.on('response', handle_response) -
内存快照分析:提取Vue/React组件状态
python复制store_state = driver.execute_script( "return window.__NUXT__.state" )
4. 反反爬实战技巧
4.1 指纹伪装方案
通过实测发现该网站会检测以下特征:
- WebGL渲染器hash
- 音频上下文指纹
- 字体枚举列表
我们的应对策略:
python复制# Playwright启动配置
browser = await playwright.chromium.launch(
headless=True,
args=[
'--disable-blink-features=AutomationControlled',
'--disable-web-security'
],
executable_path='/path/to/custom/chrome'
)
# Selenium指纹覆盖
options.add_argument("--disable-web-security")
options.add_experimental_option(
"excludeSwitches", ["enable-automation"]
)
options.add_experimental_option(
"useAutomationExtension", False
)
4.2 验证码破解方案
遇到验证码时的处理流程:
- 自动识别验证码类型(滑块/点选/旋转)
- 调用第三方打码平台(仅限紧急情况)
- 触发人工干预回调
- 自动保存验证页面快照供后续分析
python复制def handle_captcha(page):
if page.locator('.geetest_slider'):
return 'slider'
elif page.locator('.captcha-point'):
return 'click'
else:
return 'unknown'
5. 系统架构与性能优化
5.1 分布式爬虫架构
code复制[任务调度中心]
├─ [Redis任务队列]
├─ [Selenium Worker集群]
└─ [Playwright Worker集群]
├─ 自动IP轮换
├─ 浏览器指纹管理
└─ 异常熔断机制
5.2 关键性能指标
经过优化后的采集效率:
- 单节点日均采集量:12万条
- 数据完整率:99.3%
- 被封锁率:<0.5%
6. 踩坑实录与解决方案
6.1 内存泄漏问题
现象:运行8小时后Chromedriver进程内存占用超过4GB
解决方案:
python复制# 每处理100个页面强制重启浏览器
if request_count % 100 == 0:
driver.quit()
driver = init_driver()
6.2 元素定位失效
特定情况下XPath定位不到元素,原因是:
- 元素在Shadow DOM内
- 页面存在多个同class元素
改进方案:
python复制# 使用Playwright的穿透选择器
price = page.locator(
'::-p-text(¥128)'
).first.text_content()
7. 数据存储与清洗
7.1 数据结构设计
json复制{
"shop_id": "加密哈希值",
"name": "商户名称",
"rating": 4.5,
"reviews": [
{
"user": "匿名用户",
"content": "评价内容",
"photos": ["URL1", "URL2"]
}
],
"location": {
"lng": 121.4737,
"lat": 31.2304
}
}
7.2 数据去重方案
采用布隆过滤器+Redis Set双重去重:
python复制def is_duplicate(item_id):
if redis.sismember('processed_items', item_id):
return True
if bloom_filter.check(item_id):
redis.sadd('processed_items', item_id)
return True
return False
整个项目最耗时的其实不是技术实现,而是反爬策略的持续对抗。建议每天预留2小时分析新的反爬机制,保持代码库的持续更新。对于商业级爬虫,可以考虑引入机器学习自动识别页面变化模式。
code复制