使用Puppeteer批量监控网站的内存占用优化方案问询
优化Puppeteer批量页面监控的内存占用方案
看起来你在Puppeteer的资源优化上踩了两个常见的坑——要么过度隔离(Docker每个实例一个浏览器),要么无限制并行(一次性开100个浏览器实例)。我来帮你拆解问题,然后给出几个能大幅降低内存占用的优化方案:
核心问题分析
你当前的代码有两个致命的内存浪费点:
- 无限制并行执行:循环里直接调用
run()但没有await,导致100个浏览器实例几乎同时启动,瞬间把内存拉满。 - 重复创建浏览器实例:每个
run()都启动一个全新的浏览器,而浏览器本身就是内存大户——启动一个浏览器的开销远大于在同一个浏览器里创建新页面的开销。
优化方案一:复用单个浏览器实例+控制并发数
这是最有效的优化方式,通过复用一个浏览器实例来处理所有页面,同时限制并发页面数量,避免内存过载。
示例代码
const puppeteer = require('puppeteer'); const pLimit = require('p-limit'); // 用这个库轻松控制并发量,也可以自己实现 // 控制并发页面数,建议根据服务器内存调整(比如10-20个) const concurrentLimit = 10; const limit = pLimit(concurrentLimit); async function processPage(browser, url) { let page = null; try { const startTime = new Date(); console.log(`开始处理 ${url} | ${startTime}`); // 在复用的浏览器中创建新页面 page = await browser.newPage(); // 访问目标页面,等待网络空闲 await page.goto(`https://webpage.com/${url}`, { waitUntil: 'networkidle2' }); // 等待5分钟(根据需求调整) await page.waitFor(5 * 60 * 1000); // 获取页面内容(如果只需要特定数据,建议用page.evaluate()提取,更省内存) const html = await page.content(); // 这里可以添加你的数据处理逻辑(比如保存到数据库、分析内容等) const endTime = new Date(); console.log(`完成处理 ${url} | ${endTime}`); } catch (error) { console.error(`处理 ${url} 出错:`, error); } finally { // 无论成功失败,都必须关闭页面释放资源 if (page) await page.close(); } } async function main() { // 启动单个浏览器实例,配置内存优化参数 const browser = await puppeteer.launch({ args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', // 解决Linux下/dev/shm空间不足的问题,减少内存占用 '--disable-accelerated-2d-canvas', // 禁用硬件加速画布 '--no-first-run', // 跳过首次运行初始化 '--no-default-browser-check', // 跳过默认浏览器检查 '--disable-background-networking', // 禁用后台网络请求 '--disable-renderer-backgrounding', // 禁止渲染器进入后台模式 '--start-maximized=false' // 不要最大化窗口 ], headless: 'new', // 使用新版无头模式(Puppeteer v19+支持),比旧版更省内存 defaultViewport: { width: 1280, height: 720 } // 指定固定视口,避免默认大视口占用额外内存 }); try { // 假设webpage是你的100个目标URL数组 const webpage = [/* 你的URL列表 */]; // 用并发限制批量处理所有URL await Promise.all(webpage.map(url => limit(() => processPage(browser, url)))); } catch (error) { console.error('主进程出错:', error); } finally { // 所有任务完成后关闭浏览器 await browser.close(); } } // 启动主程序 main();
优化方案二:分批次复用浏览器(应对反爬场景)
如果目标网站有反爬机制,限制单个浏览器的页面数量,你可以分批次创建浏览器实例,比如每次创建5个浏览器,每个处理20个页面,处理完一批再关闭浏览器并启动下一批。这种方式的内存开销远低于100个独立容器。
额外内存优化技巧
- 避免获取完整HTML:如果只需要页面中的特定数据,用
page.evaluate(() => { /* 提取数据逻辑 */ })直接在页面中提取,不要获取整个HTML字符串,减少内存占用。 - 定期清理内存:如果长时间运行,可以定期重启浏览器实例(比如每处理50个页面重启一次),避免内存泄漏。
- 监控内存使用:用
process.memoryUsage()实时监控Node.js进程的内存占用,动态调整并发数。
为什么Docker Swarm方案内存过高?
每个Docker容器运行一个完整的浏览器实例,100个实例就是100份浏览器的内存开销(150-170MB/个),总内存轻松突破15GB。而复用浏览器实例的方案,一个浏览器处理100个页面(控制并发),总内存通常能控制在2-5GB以内,资源利用率提升数倍。
内容的提问来源于stack exchange,提问作者vsystem
相关产品推荐
相关产品推荐

