DEV Community

Cover image for How to Take Clean Website Screenshots Without Cookie Banners (Node.js + Playwright)
Umberto Richetti
Umberto Richetti

Posted on

How to Take Clean Website Screenshots Without Cookie Banners (Node.js + Playwright)

How to Take Clean Website Screenshots Without Cookie Banners

Ever tried to take automated screenshots of websites, only to have them ruined by massive "Accept All Cookies" banners?

I was building a link preview feature for a side project and quickly discovered that 90% of websites now show these GDPR consent dialogs. Every screenshot looked like this:

┌─────────────────────────────────────────┐
│  [ACTUAL WEBSITE CONTENT - 40%]         │
├─────────────────────────────────────────┤
│                                         │
│   🍪 We use cookies to improve your     │
│   experience. Please accept our         │
│   privacy policy to continue.           │
│                                         │
│   [ACCEPT ALL]  [CUSTOMIZE]  [REJECT]   │
│                                         │
│                 - 60% of the screen -   │
└─────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Not exactly the clean preview I was going for.

The Solution: Automatic Cookie Banner Removal

After trying several existing APIs (most don't handle this well, or charge premium prices), I decided to build my own solution using Node.js and Playwright.

The key insight: cookie banners follow predictable patterns that can be detected and removed before taking the screenshot.

How It Works

1. Detect Cookie Banners

Cookie consent dialogs typically share common characteristics:

const COOKIE_SELECTORS = [
  // By ID
  '#cookie-consent',
  '#cookie-banner',
  '#gdpr-banner',
  '#cookieConsent',
  '#onetrust-consent-sdk',

  // By class
  '.cookie-banner',
  '.cookie-consent',
  '.gdpr-popup',
  '.consent-banner',

  // By common button text patterns
  '[aria-label*="cookie"]',
  '[aria-label*="consent"]'
];
Enter fullscreen mode Exit fullscreen mode

2. Remove Before Screenshot

Instead of clicking "Accept" (which may set tracking cookies), we simply hide the elements:

async function removeCookieBanners(page) {
  await page.evaluate(() => {
    const selectors = [
      '#cookie-consent', '#gdpr-banner', '#onetrust-consent-sdk',
      '.cookie-banner', '.cookie-consent', '.consent-banner',
      '[class*="cookie"]', '[class*="consent"]', '[id*="cookie"]'
    ];

    selectors.forEach(selector => {
      document.querySelectorAll(selector).forEach(el => {
        el.style.display = 'none';
      });
    });

    // Also check for fixed position overlays
    document.querySelectorAll('*').forEach(el => {
      const style = getComputedStyle(el);
      if (style.position === 'fixed' && style.zIndex > 999) {
        const text = el.textContent?.toLowerCase() || '';
        if (text.includes('cookie') || text.includes('consent') || text.includes('gdpr')) {
          el.style.display = 'none';
        }
      }
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

3. Take the Screenshot

const { chromium } = require('playwright');

async function takeCleanScreenshot(url, options = {}) {
  const browser = await chromium.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });

  const page = await browser.newPage();
  await page.setViewportSize({ 
    width: options.width || 1280, 
    height: options.height || 800 
  });

  await page.goto(url, { waitUntil: 'networkidle' });

  // Remove cookie banners
  await removeCookieBanners(page);

  // Small delay to let DOM update
  await page.waitForTimeout(500);

  const screenshot = await page.screenshot({
    type: options.format || 'png',
    fullPage: options.fullPage || false
  });

  await browser.close();
  return screenshot;
}
Enter fullscreen mode Exit fullscreen mode

Full API Implementation

I wrapped this into a proper Express API with:

  • Concurrency control (limit parallel screenshots to prevent memory issues)
  • Random User-Agent rotation (avoid bot detection)
  • Resource blocking (skip videos/large assets for speed)
  • Graceful error handling

Here's the simplified architecture:

POST /api/screenshot
{
  "url": "https://example.com",
  "width": 1280,
  "height": 800,
  "fullPage": false,
  "format": "png",
  "removeCookieBanners": true
}

→ Returns: PNG/JPEG image buffer
Enter fullscreen mode Exit fullscreen mode

Results

Before and after comparison:

Metric Without Cookie Removal With Cookie Removal
Usable for previews ❌ 30% ✅ 95%+
Professional look ❌ No ✅ Yes
Extra processing time - +200ms

Try It Yourself

I deployed this as a free API on RapidAPI if you want to test it without setting up your own infrastructure:

Screenshot & Metadata API on RapidAPI

Free tier includes 50 screenshots/month - enough for testing and small projects.

Key Takeaways

  1. Cookie banners follow patterns - They use predictable class names, IDs, and positioning
  2. Hide, don't click - Hiding elements is faster and doesn't trigger tracking
  3. Check z-index + fixed positioning - Overlays typically have high z-index and fixed position
  4. Allow for edge cases - Some sites use unique selectors; consider a fallback mechanism

What's Next?

I'm considering adding:

  • Chat widget removal (Intercom, Drift, etc.)
  • Ad blocking
  • Custom CSS injection
  • Mobile device emulation

Would any of these be useful to you? Let me know in the comments!


Have you dealt with cookie banner issues in your projects? What solutions have you tried?

Top comments (0)