1. Puppeteer 特点
Puppeteer 是一个强大的 Web 爬虫工具,具有以下主要特点:
- 控制浏览器: 可以启动和控制 Headless(无头)或有头的 Chrome/Chromium 浏览器实例。
- 执行 JavaScript: 能够处理和渲染需要执行 JavaScript 的动态页面。
- 模拟用户操作: 可以模拟用户在浏览器中的各种操作,如点击、输入、导航等。
- 适用于动态内容: 特别适合那些需要执行 JavaScript 才能获取内容的页面。
2. 性能优化
提升 Puppeteer 爬取性能的主要方案包括:
2.1 禁用不必要的资源加载
1 2 3 4 5 6 7 8
| await page.setRequestInterception(true); page.on('request', (req) => { if (['image', 'stylesheet', 'font'].includes(req.resourceType())) { req.abort(); } else { req.continue(); } });
|
2.2 使用无头模式(Headless)
1
| const browser = await puppeteer.launch({ headless: true });
|
2.3 并行化操作
使用puppeteer-cluster
库来实现并行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const { Cluster } = require('puppeteer-cluster');
const cluster = await Cluster.launch({ concurrency: Cluster.CONCURRENCY_CONTEXT, maxConcurrency: 5, });
await cluster.task(async ({ page, data: url }) => { await page.goto(url); });
cluster.queue('http://example.com');
|
2.4 使用持久化浏览器实例
1
| const browser = await puppeteer.launch({ userDataDir: './user_data' });
|
2.5 设置合理的超时和重试机制
1 2
| const page = await browser.newPage(); await page.goto(url, { timeout: 30000, waitUntil: 'networkidle0' });
|
2.6 优化页面加载和等待策略
1
| await page.goto(url, { waitUntil: 'domcontentloaded' });
|
2.7 调整浏览器配置
1 2 3
| const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'] });
|
3. 反爬虫策略及解决方法
3.1 用户代理检测
解决方法:伪装用户代理
1
| await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
|
3.2 IP 限制
解决方法:使用代理池
1 2 3
| const browser = await puppeteer.launch({ args: ['--proxy-server=http://proxy-server-address:port'] });
|
3.3 设备指纹
解决方法:无头浏览器-伪装设备指纹
1 2 3 4 5
| await page.evaluateOnNewDocument(() => { Object.defineProperty(navigator, 'webdriver', { get: () => undefined, }); });
|
3.4 验证码
解决方法:对于图片验证码,可以使用打码平台或机器学习方法。对于滑动验证码,可以使用以下流程:
- 加载完成后截图
- 识别旋转角度(使用打码平台或机器学习,或从页面属性中获取)
- 使用 sharp 控制旋转
- 替换原图
3.5 动态内容加载
解决方法:使用 Puppeteer 等无头浏览器模拟浏览器环境执行 JavaScript
3.6 访问频率控制
解决方法:降低抓取频率,添加随机延迟
1
| await page.waitForTimeout(Math.floor(Math.random() * 3000) + 1000);
|
3.7 内容混淆
解决方法:分析并解密混淆的内容
3.8 行为分析
解决方法:使用 Puppeteer 模拟人类用户行为
1 2
| await page.hover('selector'); await page.click('selector');
|
4. 错误处理和稳定性
4.1 爬取失败重试
使用指数退避(Exponential Backoff)策略:
1 2 3 4 5 6 7 8 9
| const retry = async (fn, retries = 3, delay = 1000) => { try { return await fn(); } catch (err) { if (retries <= 0) throw err; await new Promise(resolve => setTimeout(resolve, delay)); return retry(fn, retries - 1, delay * 2); } };
|
4.2 子进程崩溃
- 使用 PM2 监控和自动重启
- 使用 Puppeteer Cluster 管理浏览器实例
- 实施健壮性措施:
4.3 内存泄漏预防
- 及时关闭浏览器实例和页面
- 使用
puppeteer-cluster
进行资源管理
- 避免全局变量和未释放的对象
- 定期重启浏览器实例
1 2
| await page.close(); await browser.close();
|
4.4 判断僵尸进程
可以使用以下命令检查僵尸进程:
或
1
| pgrep -l -x ".*" | grep -w Z
|
5. 最佳实践
- 遵守网站的
robots.txt
规则和使用条款。
- 实现合理的请求间隔和并发限制。
- 定期更新和维护爬虫代码,以适应目标网站的变化。
- 实现全面的错误处理和日志记录。
- 考虑使用数据库或队列系统来管理大规模爬取任务。
- 定期检查和优化性能,特别是在大规模爬取时。
通过遵循这些最佳实践和技巧,你可以构建一个高效、稳定且难以被检测的 Puppeteer 爬虫系统。