# Puppeteer Quick Reference Complete guide to browser automation with Puppeteer - a high-level API over Chrome DevTools Protocol. ## Table of Contents - [Setup](#setup) - [Browser & Page Management](#browser--page-management) - [Navigation](#navigation) - [Element Interaction](#element-interaction) - [JavaScript Execution](#javascript-execution) - [Screenshots & PDFs](#screenshots--pdfs) - [Network Interception](#network-interception) - [Device Emulation](#device-emulation) - [Performance](#performance) - [Common Patterns](#common-patterns) --- ## Setup ### Installation ```bash # Install Puppeteer npm install puppeteer # Install core only (bring your own Chrome) npm install puppeteer-core ``` ### Basic Usage ```javascript import puppeteer from 'puppeteer'; // Launch browser const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] }); // Open page const page = await browser.newPage(); // Navigate await page.goto('https://example.com'); // Do work... // Cleanup await browser.close(); ``` --- ## Browser & Page Management ### Launch Browser ```javascript const browser = await puppeteer.launch({ // Visibility headless: false, // Show browser UI headless: 'new', // New headless mode (Chrome 112+) // Chrome location executablePath: '/path/to/chrome', channel: 'chrome', // or 'chrome-canary', 'chrome-beta' // Browser context userDataDir: './user-data', // Persistent profile // Window size defaultViewport: { width: 1920, height: 1080, deviceScaleFactor: 1, isMobile: false }, // Advanced options args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-web-security', '--disable-features=IsolateOrigins', '--disable-site-isolation-trials', '--start-maximized' ], // Debugging devtools: true, // Open DevTools automatically slowMo: 250, // Slow down by 250ms per action // Network proxy: { server: 'http://proxy.com:8080' } }); ``` ### Connect to Running Browser ```javascript // Launch Chrome with debugging // google-chrome --remote-debugging-port=9222 const browser = await puppeteer.connect({ browserURL: 'http://localhost:9222', // or browserWSEndpoint: 'ws://localhost:9222/devtools/browser/...' }); ``` ### Page Management ```javascript // Create new page const page = await browser.newPage(); // Get all pages const pages = await browser.pages(); // Close page await page.close(); // Multiple pages const page1 = await browser.newPage(); const page2 = await browser.newPage(); // Switch between pages await page1.bringToFront(); ``` ### Browser Context (Incognito) ```javascript // Create isolated context const context = await browser.createBrowserContext(); const page = await context.newPage(); // Cleanup context await context.close(); ``` --- ## Navigation ### Basic Navigation ```javascript // Navigate to URL await page.goto('https://example.com'); // Navigate with options await page.goto('https://example.com', { waitUntil: 'networkidle2', // or 'load', 'domcontentloaded', 'networkidle0' timeout: 30000 // Max wait time (ms) }); // Reload page await page.reload({ waitUntil: 'networkidle2' }); // Navigation history await page.goBack(); await page.goForward(); // Wait for navigation await page.waitForNavigation({ waitUntil: 'networkidle2' }); ``` ### Wait Until Options - `load` - Wait for load event - `domcontentloaded` - Wait for DOMContentLoaded event - `networkidle0` - Wait until no network connections for 500ms - `networkidle2` - Wait until max 2 network connections for 500ms --- ## Element Interaction ### Selectors ```javascript // CSS selectors await page.$('#id'); await page.$('.class'); await page.$('div > p'); // XPath await page.$x('//button[text()="Submit"]'); // Get all matching elements await page.$$('.item'); await page.$$x('//div[@class="item"]'); ``` ### Click Elements ```javascript // Click by selector await page.click('.button'); // Click with options await page.click('.button', { button: 'left', // or 'right', 'middle' clickCount: 1, // 2 for double-click delay: 100 // Delay between mousedown and mouseup }); // ElementHandle click const button = await page.$('.button'); await button.click(); ``` ### Type Text ```javascript // Type into input await page.type('#search', 'query text'); // Type with delay await page.type('#search', 'slow typing', { delay: 100 }); // Clear and type await page.$eval('#search', el => el.value = ''); await page.type('#search', 'new text'); ``` ### Form Interaction ```javascript // Fill input await page.type('#username', 'john@example.com'); await page.type('#password', 'secret123'); // Select dropdown option await page.select('#country', 'US'); // By value await page.select('#country', 'USA', 'UK'); // Multiple // Check/uncheck checkbox await page.click('input[type="checkbox"]'); // Choose radio button await page.click('input[value="option2"]'); // Upload file const input = await page.$('input[type="file"]'); await input.uploadFile('/path/to/file.pdf'); // Submit form await page.click('button[type="submit"]'); await page.waitForNavigation(); ``` ### Hover & Focus ```javascript // Hover over element await page.hover('.menu-item'); // Focus element await page.focus('#input'); // Blur await page.$eval('#input', el => el.blur()); ``` ### Drag & Drop ```javascript const source = await page.$('.draggable'); const target = await page.$('.drop-zone'); await source.drag(target); await source.drop(target); ``` --- ## JavaScript Execution ### Evaluate in Page Context ```javascript // Execute JavaScript const title = await page.evaluate(() => document.title); // With arguments const text = await page.evaluate( (selector) => document.querySelector(selector).textContent, '.heading' ); // Return complex data const data = await page.evaluate(() => ({ title: document.title, url: location.href, cookies: document.cookie })); // With ElementHandle const element = await page.$('.button'); const text = await page.evaluate(el => el.textContent, element); ``` ### Query & Modify DOM ```javascript // Get element property const value = await page.$eval('#input', el => el.value); // Get multiple elements const items = await page.$$eval('.item', elements => elements.map(el => el.textContent) ); // Modify element await page.$eval('#input', (el, value) => { el.value = value; }, 'new value'); // Add class await page.$eval('.element', el => el.classList.add('active')); ``` ### Expose Functions ```javascript // Expose Node.js function to page await page.exposeFunction('md5', (text) => crypto.createHash('md5').update(text).digest('hex') ); // Call from page context const hash = await page.evaluate(async () => { return await window.md5('hello world'); }); ``` --- ## Screenshots & PDFs ### Screenshots ```javascript // Full page screenshot await page.screenshot({ path: 'screenshot.png', fullPage: true }); // Viewport screenshot await page.screenshot({ path: 'viewport.png', fullPage: false }); // Element screenshot const element = await page.$('.chart'); await element.screenshot({ path: 'chart.png' }); // Screenshot options await page.screenshot({ path: 'page.png', type: 'png', // or 'jpeg', 'webp' quality: 80, // JPEG quality (0-100) clip: { // Crop region x: 0, y: 0, width: 500, height: 500 }, omitBackground: true // Transparent background }); // Screenshot to buffer const buffer = await page.screenshot(); ``` ### PDF Generation ```javascript // Generate PDF await page.pdf({ path: 'page.pdf', format: 'A4', // or 'Letter', 'Legal', etc. printBackground: true, margin: { top: '1cm', right: '1cm', bottom: '1cm', left: '1cm' } }); // Custom page size await page.pdf({ path: 'custom.pdf', width: '8.5in', height: '11in', landscape: true }); // Header and footer await page.pdf({ path: 'report.pdf', displayHeaderFooter: true, headerTemplate: '
Header
', footerTemplate: '
Page
' }); ``` --- ## Network Interception ### Request Interception ```javascript // Enable request interception await page.setRequestInterception(true); // Intercept requests page.on('request', (request) => { // Block specific resource types if (request.resourceType() === 'image') { request.abort(); } // Block URLs else if (request.url().includes('ads')) { request.abort(); } // Modify request else if (request.url().includes('api')) { request.continue({ headers: { ...request.headers(), 'Authorization': 'Bearer token' } }); } // Continue normally else { request.continue(); } }); ``` ### Mock Responses ```javascript await page.setRequestInterception(true); page.on('request', (request) => { if (request.url().includes('/api/user')) { request.respond({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 1, name: 'Mock User' }) }); } else { request.continue(); } }); ``` ### Monitor Network ```javascript // Track requests page.on('request', (request) => { console.log('Request:', request.method(), request.url()); }); // Track responses page.on('response', (response) => { console.log('Response:', response.status(), response.url()); }); // Track failed requests page.on('requestfailed', (request) => { console.log('Failed:', request.failure().errorText, request.url()); }); // Get response body page.on('response', async (response) => { if (response.url().includes('/api/data')) { const json = await response.json(); console.log('API Data:', json); } }); ``` --- ## Device Emulation ### Predefined Devices ```javascript import { devices } from 'puppeteer'; // Emulate iPhone const iPhone = devices['iPhone 13 Pro']; await page.emulate(iPhone); // Common devices const iPad = devices['iPad Pro']; const pixel = devices['Pixel 5']; const galaxy = devices['Galaxy S9+']; // Navigate after emulation await page.goto('https://example.com'); ``` ### Custom Device ```javascript await page.emulate({ viewport: { width: 375, height: 812, deviceScaleFactor: 3, isMobile: true, hasTouch: true, isLandscape: false }, userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)...' }); ``` ### Viewport Only ```javascript await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 1 }); ``` ### Geolocation ```javascript // Set geolocation await page.setGeolocation({ latitude: 37.7749, longitude: -122.4194, accuracy: 100 }); // Grant permissions const context = browser.defaultBrowserContext(); await context.overridePermissions('https://example.com', ['geolocation']); ``` ### Timezone & Locale ```javascript // Set timezone await page.emulateTimezone('America/New_York'); // Set locale await page.emulateMediaType('screen'); await page.evaluateOnNewDocument(() => { Object.defineProperty(navigator, 'language', { get: () => 'en-US' }); }); ``` --- ## Performance ### CPU & Network Throttling ```javascript // CPU throttling const client = await page.createCDPSession(); await client.send('Emulation.setCPUThrottlingRate', { rate: 4 }); // Network throttling await page.emulateNetworkConditions({ offline: false, downloadThroughput: 1.5 * 1024 * 1024 / 8, // 1.5 Mbps uploadThroughput: 750 * 1024 / 8, // 750 Kbps latency: 40 // 40ms RTT }); // Predefined profiles await page.emulateNetworkConditions( puppeteer.networkConditions['Fast 3G'] ); // Disable throttling await page.emulateNetworkConditions({ offline: false, downloadThroughput: -1, uploadThroughput: -1, latency: 0 }); ``` ### Performance Metrics ```javascript // Get metrics const metrics = await page.metrics(); console.log(metrics); // { // Timestamp, Documents, Frames, JSEventListeners, // Nodes, LayoutCount, RecalcStyleCount, // LayoutDuration, RecalcStyleDuration, // ScriptDuration, TaskDuration, // JSHeapUsedSize, JSHeapTotalSize // } ``` ### Performance Tracing ```javascript // Start tracing await page.tracing.start({ path: 'trace.json', categories: [ 'devtools.timeline', 'disabled-by-default-devtools.timeline' ] }); // Navigate await page.goto('https://example.com'); // Stop tracing await page.tracing.stop(); // Analyze trace in chrome://tracing ``` ### Coverage (Code Usage) ```javascript // Start JS coverage await page.coverage.startJSCoverage(); // Start CSS coverage await page.coverage.startCSSCoverage(); // Navigate await page.goto('https://example.com'); // Stop and get coverage const jsCoverage = await page.coverage.stopJSCoverage(); const cssCoverage = await page.coverage.stopCSSCoverage(); // Calculate unused bytes let totalBytes = 0; let usedBytes = 0; for (const entry of [...jsCoverage, ...cssCoverage]) { totalBytes += entry.text.length; for (const range of entry.ranges) { usedBytes += range.end - range.start - 1; } } console.log(`Used: ${usedBytes / totalBytes * 100}%`); ``` --- ## Common Patterns ### Wait for Elements ```javascript // Wait for selector await page.waitForSelector('.element', { visible: true, timeout: 5000 }); // Wait for XPath await page.waitForXPath('//button[text()="Submit"]'); // Wait for function await page.waitForFunction( () => document.querySelector('.loading') === null, { timeout: 10000 } ); // Wait for timeout await page.waitForTimeout(2000); ``` ### Handle Dialogs ```javascript // Alert, confirm, prompt page.on('dialog', async (dialog) => { console.log(dialog.type(), dialog.message()); // Accept await dialog.accept(); // or reject // await dialog.dismiss(); // or provide input for prompt // await dialog.accept('input text'); }); ``` ### Handle Downloads ```javascript // Set download path const client = await page.createCDPSession(); await client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: '/path/to/downloads' }); // Trigger download await page.click('a[download]'); ``` ### Multiple Pages (Tabs) ```javascript // Listen for new pages browser.on('targetcreated', async (target) => { if (target.type() === 'page') { const newPage = await target.page(); console.log('New page opened:', newPage.url()); } }); // Click link that opens new tab const [newPage] = await Promise.all([ new Promise(resolve => browser.once('targetcreated', target => resolve(target.page()))), page.click('a[target="_blank"]') ]); console.log('New page URL:', newPage.url()); ``` ### Frames (iframes) ```javascript // Get all frames const frames = page.frames(); // Find frame by name const frame = page.frames().find(f => f.name() === 'myframe'); // Find frame by URL const frame = page.frames().find(f => f.url().includes('example.com')); // Main frame const mainFrame = page.mainFrame(); // Interact with frame await frame.click('.button'); await frame.type('#input', 'text'); ``` ### Infinite Scroll ```javascript async function autoScroll(page) { await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; const distance = 100; const timer = setInterval(() => { const scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } }, 100); }); }); } await autoScroll(page); ``` ### Cookies ```javascript // Get cookies const cookies = await page.cookies(); // Set cookies await page.setCookie({ name: 'session', value: 'abc123', domain: 'example.com', path: '/', httpOnly: true, secure: true, sameSite: 'Strict' }); // Delete cookies await page.deleteCookie({ name: 'session' }); ``` ### Local Storage ```javascript // Set localStorage await page.evaluate(() => { localStorage.setItem('key', 'value'); }); // Get localStorage const value = await page.evaluate(() => { return localStorage.getItem('key'); }); // Clear localStorage await page.evaluate(() => localStorage.clear()); ``` ### Error Handling ```javascript try { await page.goto('https://example.com', { waitUntil: 'networkidle2', timeout: 30000 }); } catch (error) { if (error.name === 'TimeoutError') { console.error('Page load timeout'); } else { console.error('Navigation failed:', error); } // Take screenshot on error await page.screenshot({ path: 'error.png' }); } ``` ### Stealth Mode (Avoid Detection) ```javascript // Hide automation indicators await page.evaluateOnNewDocument(() => { // Override navigator.webdriver Object.defineProperty(navigator, 'webdriver', { get: () => false }); // Mock chrome object window.chrome = { runtime: {} }; // Mock permissions const originalQuery = window.navigator.permissions.query; window.navigator.permissions.query = (parameters) => ( parameters.name === 'notifications' ? Promise.resolve({ state: 'granted' }) : originalQuery(parameters) ); }); // Set realistic user agent await page.setUserAgent( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' ); ``` --- ## Debugging Tips ### Take Screenshots on Error ```javascript page.on('pageerror', async (error) => { console.error('Page error:', error); await page.screenshot({ path: `error-${Date.now()}.png` }); }); ``` ### Console Logging ```javascript // Forward console to Node page.on('console', (msg) => { console.log('PAGE LOG:', msg.text()); }); ``` ### Slow Down Execution ```javascript const browser = await puppeteer.launch({ slowMo: 250 // 250ms delay between actions }); ``` ### Keep Browser Open ```javascript const browser = await puppeteer.launch({ headless: false, devtools: true }); // Prevent auto-close await page.evaluate(() => debugger); ``` --- ## Best Practices 1. **Always close browser:** Use try/finally or process cleanup 2. **Wait appropriately:** Use waitForSelector, not setTimeout 3. **Handle errors:** Wrap navigation in try/catch 4. **Optimize selectors:** Use specific selectors for reliability 5. **Avoid race conditions:** Wait for navigation after clicks 6. **Reuse pages:** Don't create new pages unnecessarily 7. **Set timeouts:** Always specify reasonable timeouts 8. **Clean up:** Close unused pages and contexts --- ## Resources - [Puppeteer Documentation](https://pptr.dev/) - [Puppeteer API](https://pptr.dev/api) - [Puppeteer Examples](https://github.com/puppeteer/puppeteer/tree/main/examples) - [Awesome Puppeteer](https://github.com/transitive-bullshit/awesome-puppeteer)