Blog 7 min read

How to Block Cookie Banners Programmatically: Developer Guide

Remove cookie consent popups from automated screenshots and web scraping. Covers CSS injection, request interception, and API-based solutions.

SnapRender Team
|

How to Block Cookie Banners Programmatically

Cookie consent banners are the bane of automated web workflows. Whether you're taking screenshots, scraping content, running visual regression tests, or generating thumbnails, that GDPR popup covers half the page and ruins your output.

This guide covers every practical approach to removing cookie banners programmatically, from CSS injection to request interception to using an API that handles it for you.

Why Cookie Banners Are Hard to Remove

Cookie banners aren't a single standard. Every website implements them differently:

  • Custom implementations: Hand-built HTML/CSS unique to each site
  • Consent Management Platforms (CMPs): OneTrust, Cookiebot, TrustArc, Quantcast, Didomi, each with different DOM structures
  • Google Consent Mode: Google's own consent UI, rendered differently by language and region
  • Overlay vs. banner: Some block the entire page with a modal overlay, others show a bottom bar
  • Dynamic loading: Many banners load asynchronously after the page renders, with varying delays

No single CSS selector or script removes all of them. You need a multi-layered approach.

Method 1: CSS Injection (Hide Known Selectors)

The simplest approach: inject CSS that hides known cookie banner elements.

Puppeteer

import puppeteer from 'puppeteer';

const COOKIE_BANNER_SELECTORS = [
  // OneTrust
  '#onetrust-consent-sdk',
  '#onetrust-banner-sdk',
  '.onetrust-pc-dark-filter',
  // Cookiebot
  '#CybotCookiebotDialog',
  '#CybotCookiebotDialogBodyUnderlay',
  // TrustArc
  '.truste_overlay',
  '.truste_box_overlay',
  '#truste-consent-track',
  // Quantcast
  '.qc-cmp2-container',
  '#qc-cmp2-container',
  // Didomi
  '#didomi-host',
  '#didomi-popup',
  // Google Consent
  '.fc-consent-root',
  '.fc-dialog-overlay',
  // Common patterns
  '[class*="cookie-banner"]',
  '[class*="cookie-consent"]',
  '[class*="cookie-notice"]',
  '[class*="cookie-popup"]',
  '[id*="cookie-banner"]',
  '[id*="cookie-consent"]',
  '[id*="cookie-notice"]',
  '[id*="gdpr"]',
  '[class*="consent-banner"]',
  '[class*="consent-modal"]',
  '[aria-label*="cookie"]',
  '[aria-label*="consent"]',
];

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.goto('https://example.com', { waitUntil: 'networkidle2' });

// Inject CSS to hide all known banner selectors
const hideCSS = COOKIE_BANNER_SELECTORS
  .map(s => `${s} { display: none !important; visibility: hidden !important; }`)
  .join('\n');

await page.addStyleTag({ content: hideCSS });

// Also remove any overlay/backdrop that blocks interaction
await page.addStyleTag({
  content: `
    body { overflow: auto !important; }
    html { overflow: auto !important; }
  `
});

// Wait a moment for the CSS to take effect
await new Promise(resolve => setTimeout(resolve, 500));

await page.screenshot({ path: 'clean.png' });
await browser.close();

Playwright (Python)

from playwright.sync_api import sync_playwright

COOKIE_SELECTORS = [
    "#onetrust-consent-sdk",
    "#CybotCookiebotDialog",
    ".qc-cmp2-container",
    "#didomi-host",
    ".fc-consent-root",
    '[class*="cookie-banner"]',
    '[class*="cookie-consent"]',
    '[id*="cookie-notice"]',
    '[class*="consent-banner"]',
]

hide_css = " ".join(
    f"{s} {{ display: none !important; }}" for s in COOKIE_SELECTORS
) + " body, html { overflow: auto !important; }"

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com", wait_until="networkidle")
    page.add_style_tag(content=hide_css)
    page.wait_for_timeout(500)
    page.screenshot(path="clean.png")
    browser.close()

Pros and Cons

Pros: Simple, no external dependencies, works for the majority of common CMPs.

Cons: Doesn't catch every implementation. New banner styles break the selectors. The banner's HTML is still in the DOM (hidden, not removed), which can affect layout. Overlay-style banners may still prevent scrolling unless you also fix the overflow property.

Method 2: Click the Accept Button

Instead of hiding the banner, programmatically click "Accept" or "Agree."

const ACCEPT_SELECTORS = [
  // OneTrust
  '#onetrust-accept-btn-handler',
  // Cookiebot
  '#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll',
  '#CybotCookiebotDialogBodyButtonAccept',
  // Generic patterns
  'button[id*="accept"]',
  'button[class*="accept"]',
  'a[id*="accept"]',
  'button[data-action="accept"]',
  // Text-based matching
  'button:has-text("Accept")',
  'button:has-text("Accept all")',
  'button:has-text("Agree")',
  'button:has-text("Got it")',
  'button:has-text("OK")',
  'button:has-text("I agree")',
];

async function dismissCookieBanner(page) {
  for (const selector of ACCEPT_SELECTORS) {
    try {
      const button = await page.$(selector);
      if (button) {
        await button.click();
        // Wait for banner to animate away
        await new Promise(resolve => setTimeout(resolve, 1000));
        return true;
      }
    } catch {
      // Selector not found or not clickable
    }
  }
  return false;
}

// Usage
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await dismissCookieBanner(page);
await page.screenshot({ path: 'clean.png' });

Pros: Cleanest result. The banner is genuinely dismissed and the layout adjusts naturally.

Cons: Even more fragile than CSS hiding. Button text varies by language. Some banners require waiting for animation. Clicking "Accept" may set tracking cookies you don't want.

Method 3: Block CMP Scripts via Request Interception

Prevent the cookie banner from loading in the first place by blocking the consent management scripts.

const CMP_DOMAINS = [
  'cdn.cookielaw.org',        // OneTrust
  'consent.cookiebot.com',     // Cookiebot
  'consent.trustarc.com',      // TrustArc
  'quantcast.mgr.consensu.org', // Quantcast
  'sdk.privacy-center.org',   // Didomi
  'fundingchoicesmessages.google.com', // Google Consent
  'consentmanager.net',
  'consentframework.com',
];

await page.setRequestInterception(true);

page.on('request', (request) => {
  const url = request.url();
  if (CMP_DOMAINS.some(domain => url.includes(domain))) {
    request.abort();
  } else {
    request.continue();
  }
});

await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'no-banner.png' });

Pros: The banner never appears at all. Fastest approach (no waiting for banner to load then hiding it). Also blocks tracking.

Cons: Some sites check if the CMP loaded and show a fallback banner. Blocking scripts may break site functionality (rare but possible).

Method 4: Combined Approach (Most Robust)

For production use, combine all three methods:

import puppeteer from 'puppeteer';

const CMP_DOMAINS = [
  'cdn.cookielaw.org', 'consent.cookiebot.com', 'consent.trustarc.com',
  'quantcast.mgr.consensu.org', 'sdk.privacy-center.org',
  'fundingchoicesmessages.google.com', 'consentmanager.net',
];

const BANNER_SELECTORS = [
  '#onetrust-consent-sdk', '#CybotCookiebotDialog',
  '.qc-cmp2-container', '#didomi-host', '.fc-consent-root',
  '[class*="cookie-banner"]', '[class*="cookie-consent"]',
  '[id*="cookie-notice"]', '[class*="consent-banner"]',
];

async function captureWithoutBanners(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Layer 1: Block CMP scripts
  await page.setRequestInterception(true);
  page.on('request', (request) => {
    const reqUrl = request.url();
    if (CMP_DOMAINS.some(d => reqUrl.includes(d))) {
      request.abort();
    } else {
      request.continue();
    }
  });

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

  // Layer 2: Hide remaining banners via CSS
  const hideCSS = BANNER_SELECTORS
    .map(s => `${s} { display: none !important; }`)
    .join('\n') + '\nbody, html { overflow: auto !important; }';
  await page.addStyleTag({ content: hideCSS });

  // Layer 3: Try clicking accept on anything that survived
  const acceptSelectors = [
    '#onetrust-accept-btn-handler',
    'button[id*="accept"]',
    'button[class*="accept"]',
  ];
  for (const sel of acceptSelectors) {
    try {
      const btn = await page.$(sel);
      if (btn) { await btn.click(); break; }
    } catch { /* ignore */ }
  }

  await new Promise(resolve => setTimeout(resolve, 500));
  const buffer = await page.screenshot({ type: 'png' });
  await browser.close();
  return buffer;
}

This catches ~95% of cookie banners. But maintaining this code is an ongoing burden. New CMPs appear, existing ones update their DOM structure, and regional variations add complexity.

Method 5: Use a Screenshot API with Built-In Banner Removal

If cookie banner removal is one feature of a larger workflow (screenshots, thumbnails, archiving), consider using an API that handles it automatically.

cURL

curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&block_cookie_banners=true&format=png" \
  -H "X-API-Key: YOUR_API_KEY" \
  --output clean.png

Node.js

import { SnapRender } from 'snaprender';

const client = new SnapRender('YOUR_API_KEY');

const image = await client.screenshot({
  url: 'https://example.com',
  blockCookieBanners: true,
  blockAds: true,
  format: 'png'
});

Python

from snaprender import SnapRender

client = SnapRender("YOUR_API_KEY")

image = client.screenshot(
    url="https://example.com",
    block_cookie_banners=True,
    block_ads=True,
    format="png"
)

with open("clean.png", "wb") as f:
    f.write(image)

The block_cookie_banners=true parameter (enabled by default) handles all the complexity server-side: CMP script blocking, CSS injection, accept-button clicking, and ongoing maintenance of banner selectors. You don't need to maintain any of the code shown above.

Which Approach Should You Use?

Approach Reliability Maintenance Best For
CSS injection ~80% Medium Quick scripts, one-off captures
Accept button clicking ~70% High When you need the banner genuinely dismissed
Script blocking ~85% Medium When you also want to block tracking
Combined (all three) ~95% Very high Production self-hosted systems
Screenshot API ~95%+ None Production workflows, teams

For one-off scripts: CSS injection is the fastest to implement.

For production systems: Either invest in the combined approach and maintain it, or use an API. The maintenance cost of keeping banner selectors updated across hundreds of CMP variants is significant.

SnapRender removes cookie banners by default on every screenshot. 500 free screenshots/month, no credit card required. Get your API key and stop fighting with cookie popups.

Try SnapRender Free

500 free screenshots/month, no credit card required.

Sign up free