如何在Puppeteer执行多任务时仅使用单个浏览器实例绕过反爬检测
Puppeteer 单浏览器实例多任务复用实现方案
核心思路是将浏览器实例做全局缓存,仅在首次调用时初始化,后续所有任务直接复用该实例创建新页面执行操作,完全避免重复创建浏览器触发反爬。
场景1:单脚本批量执行多任务
适合本地批量爬取多个页面的场景,代码如下:
const puppeteer = require('puppeteer'); // 全局缓存浏览器实例 let browser = null; // 浏览器初始化方法,全程仅执行一次 async function initBrowser() { if (!browser) { browser = await puppeteer.launch({ headless: 'new', // 采用新版无头模式,指纹更接近普通浏览器 args: [ '--no-sandbox', '--disable-setuid-sandbox', // 移除自动化标识,降低被检测概率 '--disable-blink-features=AutomationControlled' ] }); // 全局统一设置UA,保持指纹一致性 await browser.userAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'); } return browser; } // 单任务执行逻辑,每个任务单独创建、销毁页面 async function execTask(targetUrl) { const browser = await initBrowser(); const page = await browser.newPage(); try { // 可单独为当前页面设置视窗、cookie、代理等,不影响其他页面 await page.setViewport({ width: 1920, height: 1080 }); await page.goto(targetUrl, { timeout: 30000, waitUntil: 'networkidle2' }); // 此处编写你的爬虫业务逻辑 const pageTitle = await page.title(); console.log(`爬取成功,页面标题:${pageTitle}`); return pageTitle; } catch (err) { console.error(`爬取页面${targetUrl}失败:`, err); return null; } finally { // 任务执行完毕立即关闭页面,释放内存资源 await page.close(); } } // 批量任务调度示例 (async () => { // 待爬取的URL列表 const taskList = [ 'https://example.com/1', 'https://example.com/2', 'https://example.com/3' ]; // 控制并发数,避免同时打开过多页面导致浏览器卡顿 const concurrencyLimit = 2; for (let i = 0; i < taskList.length; i += concurrencyLimit) { const currentBatch = taskList.slice(i, i + concurrencyLimit); await Promise.all(currentBatch.map(url => execTask(url))); } // 所有任务全部执行完成后,再关闭浏览器实例 await browser.close(); })();
场景2:服务端常驻按需执行任务
适合用Express等框架做爬虫服务、接收到请求才执行爬取任务的场景,代码如下:
const express = require('express'); const puppeteer = require('puppeteer'); const app = express(); let browser = null; // 服务启动时一次性初始化浏览器实例 app.listen(3000, async () => { browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-blink-features=AutomationControlled'] }); console.log('服务启动完成,浏览器实例已初始化'); }); // 爬取接口 app.get('/crawl', async (req, res) => { const { url } = req.query; if (!url) return res.status(400).send('缺少url参数'); const page = await browser.newPage(); try { await page.goto(url, { timeout: 30000 }); const pageContent = await page.content(); res.send(pageContent); } catch (err) { res.status(500).send(`爬取失败:${err.message}`); } finally { await page.close(); } });
注意事项
- 复用浏览器实例时,每个页面的配置(代理、cookie、UA等)互相独立,可按需单独配置不会互相影响
- 建议控制单浏览器实例的并发页面数在5以内,并发过高会导致浏览器响应变慢、请求超时
- 可添加浏览器心跳检测逻辑,出现实例崩溃的情况自动重新初始化,保证服务可用性
- 除了复用浏览器实例,还需保持页面请求间隔、指纹的一致性,避免异常行为触发反爬
内容的提问来源于stack exchange,提问作者Jack Caycedo
相关产品推荐
相关产品推荐

