Volver al blog
Automatizacion

Headless Browser Screenshots: Best Practices and Tips

Practical guide to capturing consistent, high-quality screenshots with cloud browsers, covering timing, resolution, and format optimization.

Introduction

Screenshots are one of the most common outputs of browser automation: visual regression testing, content archiving, compliance monitoring, and report generation. Getting consistent, high-quality screenshots requires understanding timing, viewport configuration, and format trade-offs.

Timing: When to Capture

The most common screenshot problem is capturing too early, before the page has fully rendered.

Wait Strategies

// Wait for network to settle (no requests for 500ms)
await page.goto(url, { waitUntil: 'networkidle0' });

// Wait for a specific element
await page.waitForSelector('.content-loaded');

// Wait for fonts to load
await page.evaluate(() => document.fonts.ready);

// Wait for images to load
await page.evaluate(() => {
  return Promise.all(
    Array.from(document.images)
      .filter(img => !img.complete)
      .map(img => new Promise(resolve => {
        img.onload = img.onerror = resolve;
      }))
  );
});

// Custom wait for SPA content
await page.waitForFunction(() => {
  return document.querySelector('#app')?.__vue__?.isReady === true;
});

Combining Wait Strategies

For complex pages, combine multiple strategies:

await page.goto(url, { waitUntil: 'networkidle0' });
await page.waitForSelector('.main-content');
await page.evaluate(() => document.fonts.ready);
// Small additional delay for animations to complete
await page.evaluate(() => new Promise(r => setTimeout(r, 500)));

Viewport and Resolution

Setting Viewport

// Set viewport before navigation
await page.setViewport({
  width: 1920,
  height: 1080,
  deviceScaleFactor: 2, // For retina-quality screenshots
});

await page.goto(url);
await page.screenshot({ path: 'screenshot.png' });

Full Page Screenshots

// Capture the entire scrollable page
await page.screenshot({
  path: 'full-page.png',
  fullPage: true,
});

Element Screenshots

// Capture a specific element
const element = await page.$('.hero-section');
await element.screenshot({ path: 'hero.png' });

Format Selection

PNG vs JPEG

FactorPNGJPEG
QualityLosslessLossy
File sizeLarger (1-5 MB)Smaller (100-500 KB)
TransparencySupportedNot supported
Best forUI testing, text-heavyPhotos, visual content
// PNG (default) - best for visual comparison
await page.screenshot({ path: 'screenshot.png', type: 'png' });

// JPEG with quality control - best for archiving
await page.screenshot({
  path: 'screenshot.jpg',
  type: 'jpeg',
  quality: 80,
});

WebP

// WebP - good balance of quality and size
await page.screenshot({
  path: 'screenshot.webp',
  type: 'webp',
  quality: 85,
});

Device Pixel Ratio

Higher deviceScaleFactor produces sharper screenshots at the cost of larger file sizes:

// 1x - standard resolution (1920x1080 = ~2 MP)
await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 1 });

// 2x - retina resolution (3840x2160 = ~8 MP)
await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 2 });

For visual regression testing, 2x provides better detail for detecting subtle rendering differences.

Batch Capture Pattern

For capturing screenshots of many pages efficiently:

async function captureScreenshot(browser, url, outputPath) {
  const page = await browser.newPage();

  try {
    await page.setViewport({ width: 1920, height: 1080 });
    await page.goto(url, {
      waitUntil: 'networkidle0',
      timeout: 30000,
    });
    await page.screenshot({ path: outputPath, fullPage: true });
  } finally {
    await page.close();
  }
}

const browser = await puppeteer.connect({
  browserWSEndpoint: 'wss://bots.win/ws?apiKey=YOUR_API_KEY',
});

const urls = ['https://example.com', 'https://example.com/about', ...];

for (const url of urls) {
  const filename = url.replace(/[^a-zA-Z0-9]/g, '_') + '.png';
  await captureScreenshot(browser, url, filename);
}

await browser.close();

Best Practices

  1. Always wait for content before capturing, not just DOMContentLoaded
  2. Set viewport before navigation for consistent layout
  3. Use PNG for testing, JPEG for archiving
  4. Consider deviceScaleFactor: 2 for high-quality output
  5. Set timeouts on all navigation and wait operations
  6. Close pages after capture to free memory
#screenshots#headless#best-practices#automation