其他
何时用 Puppeteer 做浏览器自动化:网页抓取、测试…
在 Node.js 中用 Puppeteer 控制 headless/headful Chrome,完成网页抓取、交互测试、截图/PDF、网络拦截、认证会话与并行自动化时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 38/100 ×0.15
适用场景
何时用 Puppeteer 做浏览器自动特定
解决思路
- 初始化项目:运行 npm init -y 和 npm install puppeteer。
- 按基本结构编写脚本:引入 puppeteer;定义 async main;使用 puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] }) 启动;在 try 中 newPage、goto 和执行自动化;在 finally 中 browser.close();最后用 main().catch(console.error) 捕获错误。
- 配置浏览器启动选项:headless:'new' 使用新 headless 模式,false 显示浏览器;slowMo:50 放慢操作调试;devtools:true 自动打开 DevTools;args 传入 '--no-sandbox'、'--disable-setuid-sandbox'、'--disable-dev-shm-usage'、'--disable-accelerated-2d-canvas'、'--disable-gpu'、'--window-size=1920,1080';defaultViewport 设为 { width:1920, height:1080 }。
- 页面导航:调用 page.goto('https://example.com', { waitUntil:'networkidle2', timeout:30000 });waitUntil 可选 'load'、'domcontentloaded'、'networkidle0'、'networkidle2'。
- 导航历史与刷新:用 page.goBack()、page.goForward()、page.reload({ waitUntil:'networkidle2' })。
- 元素选择:用 page.$('selector') 获取单个元素,page.$$('selector') 获取多个元素,page.waitForSelector('selector', { visible:true, timeout:5000 }) 等待元素,page.$x('//xpath/expression') 使用 XPath。
- 页面上下文求值:用 page.$eval('selector', el => el.textContent) 取文本,page.$eval('a', el => el.getAttribute('href')) 取属性,page.$$eval('.items', elements => elements.map(el => el.textContent)) 批量取文本,page.evaluate(() => document.title) 执行任意 JavaScript。
- 点击操作:用 page.click('button#submit');可传 { button:'left', clickCount:1, delay:100 },button 可为 'left'、'right'、'middle';点击并等待导航用 Promise.all([page.waitForNavigation(), page.click('a.nav-link')])。
- 输入与按键:用 page.type('input#username', 'myuser', { delay:50 }) 输入;清空并重输先 page.click('input#username', { clickCount:3 }) 再 page.type;用 page.keyboard.press('Enter')、page.keyboard.down('Shift')、page.keyboard.press('Tab')、page.keyboard.up('Shift') 模拟按键。
- 表单处理:用 page.select('select#country', 'us') 选择下拉框;用 page.click('input[type=checkbox]') 勾选复选框;用 const inputFile = await page.$('input[type=file]') 后 inputFile.uploadFile('/path/to/file.pdf') 上传文件。
- 等待元素出现:用 page.waitForSelector('.loaded')。
- 等待元素消失:用 page.waitForSelector('.loading', { hidden:true })。
- 等待函数条件:用 page.waitForFunction(() => document.querySelector('.count').textContent === '10')。
- 等待导航:用 page.waitForNavigation({ waitUntil:'networkidle2' })。
- 等待网络请求:用 page.waitForRequest(request => request.url().includes('/api/data'))。
- 等待网络响应:用 page.waitForResponse(response => response.url().includes('/api/data') && response.status() === 200)。
- 固定等待:用 page.waitForTimeout(1000),但应尽量少用。
- 截图:用 page.screenshot({ path:'screenshot.png', fullPage:true }) 截全页;用 element.screenshot({ path:'chart.png' }) 截元素;可传 type:'png' 或 'jpeg'、quality:80(仅 jpeg,0-100)、clip:{ x:0, y:0, width:800, height:600 }。
- 生成 PDF:用 page.pdf({ path:'document.pdf', format:'A4', printBackground:true, margin:{ top:'20px', right:'20px', bottom:'20px', left:'20px' } })。
- 启用网络拦截:先 await page.setRequestInterception(true)。
- 拦截并阻断请求:监听 page.on('request', request => ...),用 request.resourceType() 判断;若为 'image' 或 'stylesheet' 等则 request.abort(),否则 request.continue()。
- 修改请求:在 request 事件中调用 request.continue({ headers: { ...request.headers(), 'X-Custom-Header':'value' } })。
- 监控响应:监听 page.on('response', async response => ...),若 response.url().includes('/api/') 则 const data = await response.json() 并输出。
- 认证:用 page.authenticate({ username:'user', password:'pass' }) 处理 Basic HTTP Authentication。
- 设置 Cookie:用 page.setCookie({ name:'session', value:'abc123', domain:'example.com' })。
- 获取与删除 Cookie:用 const cookies = await page.cookies() 获取;用 page.deleteCookie({ name:'session' }) 删除。
- 创建隔离上下文:用 const context = await browser.createIncognitoBrowserContext(),再 const page = await context.newPage()。
- 多页面管理:用 browser.newPage() 创建多个页面,用 browser.pages() 获取所有页面。
- 处理弹窗:监听 page.on('popup', async popup => { await popup.waitForLoadState(); console.log('Popup URL:', popup.url()); })。
- 实现错误处理与重试:编写 async function scrapeWithRetry(url, maxRetries = 3),循环尝试;每次 puppeteer.launch、browser.newPage、page.setDefaultTimeout(30000)、page.goto(url, { waitUntil:'networkidle2' })、page.$eval('.content', el => el.textContent)、browser.close() 并返回 data;catch 时输出 Attempt ${i + 1} failed: ${error.message},非最后一次等待 2000 * (i + 1) 毫秒重试,最后一次 throw error。
- 性能优化:启用请求拦截并阻断 'image'、'stylesheet'、'font'。
- 复用浏览器实例:只启动一次 const browser = await puppeteer.launch(),每次抓取 newPage,在 finally 中 page.close() 而不是关闭 browser。
- 并行抓取:使用 puppeteer-cluster 做连接池/并行抓取。
- 选择依赖:基础用 puppeteer;自定义 Chrome 安装用 puppeteer-core;并行抓取用 puppeteer-cluster;插件用 puppeteer-extra;反检测用 puppeteer-extra-plugin-stealth。
- 遵循最佳实践:始终在 finally 中关闭 browser 实例;交互前用 waitForSelector;优先 networkidle2 而不是 networkidle0;反机器人绕过用 stealth 插件;实现错误处理与重试;长运行脚本监控内存;用 browser context 隔离会话;为所有操作设置合理 timeout。
- 遵循编码原则:使用 async/await 提高可读性;用 try/catch 做错误处理;为动态内容实现健壮等待;正确关闭 browser 防止内存泄漏;模块化设计可复用自动化代码;正确处理 browser context 与 page 生命周期。
适用边界 · 注意事项
- 仅需获取静态 HTML 且无需执行 JavaScript 时,不必用 Puppeteer,改用轻量 HTTP 客户端和 HTML 解析器。
- 目标网站明确禁止自动化/抓取,或会违反服务条款、robots.txt 时,不要使用。
- 项目不是 Node.js 生态,或不愿意维护 Chrome/Chromium 与 Puppeteer 依赖时,不适合。