乐闻世界logo
搜索文章和话题

Puppeteer 如何进行错误处理和调试?有哪些常用的调试技巧和工具?

2月19日 19:40

Puppeteer 提供了多种错误处理和调试技巧,帮助开发者快速定位和解决问题,提高开发效率。

1. 基本错误处理

try-catch 模式:

javascript
const puppeteer = require('puppeteer'); async function safeExecution() { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { await page.goto('https://example.com'); await page.click('#button'); } catch (error) { console.error('Error occurred:', error.message); // 错误处理逻辑 } finally { await browser.close(); } } safeExecution();

超时处理:

javascript
try { await page.goto('https://example.com', { timeout: 5000 }); } catch (error) { if (error.name === 'TimeoutError') { console.log('Page load timeout'); } }

2. 调试模式

启用调试模式:

javascript
// 方法 1:使用 headless: false const browser = await puppeteer.launch({ headless: false, slowMo: 100 // 减慢操作速度 }); // 方法 2:使用 devtools const browser = await puppeteer.launch({ headless: false, devtools: true });

使用 slowMo:

javascript
const browser = await puppeteer.launch({ headless: false, slowMo: 50 // 每个操作延迟 50ms });

3. 日志记录

控制台日志:

javascript
page.on('console', msg => { console.log('Browser console:', msg.text()); }); // 捕获不同类型的日志 page.on('console', msg => { const type = msg.type(); const text = msg.text(); if (type === 'error') { console.error('Browser error:', text); } else if (type === 'warning') { console.warn('Browser warning:', text); } else { console.log('Browser log:', text); } });

页面错误日志:

javascript
page.on('pageerror', error => { console.error('Page error:', error.message); });

请求失败日志:

javascript
page.on('requestfailed', request => { console.log('Request failed:', request.url()); console.log('Failure:', request.failure()); });

4. 截图和视频录制

错误时截图:

javascript
async function withErrorScreenshot(page, operation) { try { await operation(); } catch (error) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); await page.screenshot({ path: `error-${timestamp}.png`, fullPage: true }); throw error; } } // 使用示例 await withErrorScreenshot(page, async () => { await page.goto('https://example.com'); await page.click('#button'); });

视频录制:

javascript
const { spawn } = require('child_process'); async function recordVideo(page, outputPath, operation) { // 使用 ffmpeg 录制屏幕 const ffmpeg = spawn('ffmpeg', [ '-f', 'x11grab', '-r', '30', '-s', '1920x1080', '-i', ':99', '-c:v', 'libx264', '-preset', 'ultrafast', outputPath ]); try { await operation(); } finally { ffmpeg.kill('SIGINT'); } }

5. 网络调试

监控网络请求:

javascript
page.on('request', request => { console.log('Request:', request.url()); }); page.on('response', response => { console.log('Response:', response.url(), response.status()); }); page.on('requestfinished', request => { console.log('Request finished:', request.url()); });

捕获请求和响应数据:

javascript
const requests = []; page.on('request', request => { requests.push({ url: request.url(), method: request.method(), headers: request.headers() }); }); page.on('response', async response => { const request = requests.find(r => r.url === response.url()); if (request) { request.status = response.status(); request.headers = response.headers(); try { request.body = await response.text(); } catch (error) { request.body = null; } } });

6. 性能追踪

启用性能追踪:

javascript
const client = await page.target().createCDPSession(); await client.send('Performance.enable'); await client.send('Network.enable'); // 获取性能指标 const metrics = await client.send('Performance.getMetrics'); console.log('Performance metrics:', metrics);

追踪时间线:

javascript
await page.tracing.start({ path: 'trace.json' }); // 执行操作 await page.goto('https://example.com'); await page.tracing.stop();

7. 元素调试

高亮元素:

javascript
async function highlightElement(page, selector) { await page.evaluate(selector => { const element = document.querySelector(selector); if (element) { element.style.border = '3px solid red'; element.style.backgroundColor = 'yellow'; } }, selector); }

检查元素状态:

javascript
async function checkElement(page, selector) { const isVisible = await page.isVisible(selector); const isEnabled = await page.isDisabled(selector); const isClickable = await page.isClickable(selector); console.log('Element state:', { selector, isVisible, isEnabled, isClickable }); }

获取元素位置:

javascript
const position = await page.evaluate(selector => { const element = document.querySelector(selector); if (element) { const rect = element.getBoundingClientRect(); return { x: rect.left, y: rect.top, width: rect.width, height: rect.height }; } }, '.element');

8. 调试工具函数

等待并调试:

javascript
async function waitForAndDebug(page, selector, options = {}) { console.log(`Waiting for selector: ${selector}`); try { await page.waitForSelector(selector, { timeout: options.timeout || 30000, visible: options.visible !== false }); console.log(`Found selector: ${selector}`); } catch (error) { console.error(`Failed to find selector: ${selector}`); await page.screenshot({ path: 'debug-failed.png' }); throw error; } }

点击并调试:

javascript
async function clickAndDebug(page, selector) { console.log(`Attempting to click: ${selector}`); try { // 检查元素是否存在 const element = await page.$(selector); if (!element) { throw new Error(`Element not found: ${selector}`); } // 检查元素是否可见 const isVisible = await element.isIntersectingViewport(); if (!isVisible) { console.warn('Element is not visible, scrolling to it'); await element.scrollIntoView(); } await element.click(); console.log(`Successfully clicked: ${selector}`); } catch (error) { console.error(`Failed to click: ${selector}`, error); await page.screenshot({ path: 'debug-click-failed.png' }); throw error; } }

9. 常见错误及解决方案

错误 1:元素未找到

javascript
// 问题:元素选择器错误 await page.click('.wrong-selector'); // 解决方案:使用正确的选择器 await page.click('.correct-selector'); // 或者等待元素出现 await page.waitForSelector('.correct-selector'); await page.click('.correct-selector');

错误 2:元素不可点击

javascript
// 问题:元素被遮挡或不可见 await page.click('.hidden-button'); // 解决方案:滚动到元素 await page.evaluate(selector => { document.querySelector(selector).scrollIntoView(); }, '.hidden-button'); await page.click('.hidden-button');

错误 3:超时错误

javascript
// 问题:页面加载超时 await page.goto('https://slow-website.com'); // 解决方案:增加超时时间 await page.goto('https://slow-website.com', { timeout: 60000 }); // 或使用更宽松的等待条件 await page.goto('https://slow-website.com', { waitUntil: 'domcontentloaded' });

错误 4:内存泄漏

javascript
// 问题:未关闭浏览器实例 const browser = await puppeteer.launch(); // 忘记关闭 // 解决方案:使用 finally 确保关闭 const browser = await puppeteer.launch(); try { // 操作 } finally { await browser.close(); }

10. 调试最佳实践

1. 使用描述性日志:

javascript
console.log(`[INFO] Navigating to ${url}`); console.log(`[DEBUG] Found ${elements.length} elements`); console.log(`[ERROR] Failed to click button: ${error.message}`);

2. 保存调试信息:

javascript
const debugInfo = { url: page.url(), timestamp: new Date().toISOString(), screenshot: await page.screenshot({ encoding: 'base64' }), html: await page.content(), cookies: await page.cookies() }; require('fs').writeFileSync('debug.json', JSON.stringify(debugInfo, null, 2));

3. 使用条件断点:

javascript
await page.evaluate(() => { debugger; // 在浏览器中暂停 });

4. 分步调试:

javascript
// 使用 slowMo 减慢操作 const browser = await puppeteer.launch({ slowMo: 100 }); // 或在关键步骤添加延迟 await new Promise(resolve => setTimeout(resolve, 1000));

5. 使用调试器:

javascript
// 在代码中添加 debugger debugger; // 使用 Node.js 调试器运行 node --inspect-brk script.js

11. 测试和验证

单元测试示例:

javascript
const assert = require('assert'); async function testPageLoad() { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com'); const title = await page.title(); assert.strictEqual(title, 'Example Domain'); await browser.close(); } testPageLoad().catch(console.error);

集成测试示例:

javascript
async function testUserFlow() { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 测试登录流程 await page.goto('https://example.com/login'); await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); // 验证登录成功 await page.waitForSelector('.user-profile'); const isLoggedIn = await page.$('.user-profile') !== null; assert(isLoggedIn, 'Login failed'); await browser.close(); } testUserFlow().catch(console.error);
标签:Puppeteer