ScreenshotNeo

BlogHow-to

How to Capture SERP Screenshots After Dismissing the Consent Dialog

Dismiss the consent interface with its actual on-page control, verify the result, and capture a SERP with Playwright. Includes reliable patterns and fixes.

By the ScreenshotNeo team4 October 20269 min read

To capture a SERP after a consent dialog, navigate to the search results URL, locate the consent interface’s real visible control, choose the action your workflow intends, wait for the interface to close, and then take the screenshot. Consent controls vary by site, language, region, and session, so there is no reliable universal button selector. The example below uses Playwright and a sample “Reject optional cookies” button name; inspect the actual page and replace it with the control and label shown there.

1. Decide what the screenshot should prove

Before automating, decide what consent choice the capture is meant to represent. If the test covers rejecting optional cookies, click the reject option; if it covers accepting or customizing consent, use that control instead. Do not accept consent simply to clear the screen. Capture only after the intended action has occurred and the page is in the expected post-choice state.

Also decide whether you need the visible viewport or the entire scrollable SERP. A normal Playwright page screenshot captures the viewport. Set fullPage: true only when the complete page is needed; it creates a taller image and is not necessary just to remove an overlay. [Playwright Page API]

2. Capture the SERP with Playwright

This runnable Node.js example opens a SERP URL, explicitly waits for the expected consent button, clicks it, verifies that it becomes hidden, and saves a viewport screenshot. Install Playwright and its Chromium browser first:

npm init -y
npm install playwright
npx playwright install chromium

Save this as capture-serp.js. Set SERP_URL to the exact results URL you want to capture. Update the role and accessible name to match the actual control on the target page. The example fails clearly if the control does not appear or does not disappear, instead of silently saving a misleading screenshot.

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

async function main() {
  const targetSerpUrl = process.env.SERP_URL;
  if (!targetSerpUrl) {
    throw new Error('Set SERP_URL to the search results URL to capture.');
  }

  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage({
    viewport: { width: 1365, height: 900 },
    deviceScaleFactor: 1,
  });

  try {
    await page.goto(targetSerpUrl, { waitUntil: 'domcontentloaded' });

    // Example only: use the real visible label and intended consent choice.
    const rejectOptional = page.getByRole('button', {
      name: 'Reject optional cookies',
      exact: true,
    });
    await rejectOptional.waitFor({ state: 'visible', timeout: 15000 });
    await rejectOptional.click();
    await rejectOptional.waitFor({ state: 'hidden', timeout: 10000 });

    // Optional page-specific check can go here, such as waiting for the results heading.
    await page.screenshot({ path: 'serp.png' });
    // For the whole scrollable page, use: { path: 'serp.png', fullPage: true }
  } finally {
    await browser.close();
  }
}

main().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Run it with:

SERP_URL='https://www.google.com/search?q=playwright' node capture-serp.js

The search engine may show different consent UI depending on locale, region, prior cookies, and how the request is routed. The sample URL and button name are examples, not a promise about a particular page’s current interface.

Choosing and locating the right control

  • Prefer an accessible role and name. A locator such as getByRole('button', { name: 'Reject optional cookies' }) describes the user-visible action and is often clearer than a brittle CSS path.
  • Inspect when the locator times out. In a headed run, inspect the page and its accessibility tree or DOM to identify the banner’s actual label and structure. If the control is inside an iframe, locate the appropriate frame first and query within it.
  • Use a CSS selector only when needed. Some consent platforms have no useful accessible name. Use a selector scoped to the banner and button, based on the current page markup. Vendor examples treat selector lists as starting points that must be adjusted for the target. [Browserless cookie-consent example]
  • Be specific about the choice. “Accept all,” “Reject optional,” and “Manage preferences” are different actions. Choose according to the test or capture requirement.

3. Make captures repeatable

For a repeatable capture, keep the browser engine and version, viewport, device scale factor, locale, timezone, and execution environment consistent. Search results and page chrome can still change over time, so identical browser settings do not make live SERPs invariant. If consent state is persisted in cookies or browser storage, decide whether each run should start with a fresh browser context or intentionally reuse a saved state. A fresh context is useful when the capture must exercise the consent dialog; reused state is useful when the test is specifically about returning visitors.

Wait for meaningful page state rather than adding an arbitrary delay whenever possible. For example, after dismissing consent, wait for a results heading or another stable element that is relevant to the target page. Avoid assuming that networkidle is always the right condition: live pages can continue background requests even after the visible result is ready.

When using Playwright Test for visual regression, toHaveScreenshot() waits until two consecutive screenshots match before comparing the last capture with its baseline. This helps with transient rendering differences, but it does not stabilize changing search results or site-specific consent behavior. Run baseline and comparison captures in the same environment. [Playwright visual comparisons]

import { test, expect } from '@playwright/test';

test('SERP is visible after rejecting optional cookies', async ({ page }) => {
  await page.goto(process.env.SERP_URL);

  const rejectOptional = page.getByRole('button', {
    name: 'Reject optional cookies',
    exact: true,
  });
  await rejectOptional.waitFor({ state: 'visible' });
  await rejectOptional.click();
  await rejectOptional.waitFor({ state: 'hidden' });

  await expect(page).toHaveScreenshot('serp.png');
});

This test uses the Playwright Test runner; it is separate from the standalone script above. For an intentionally full-page visual assertion, use the screenshot options supported by the installed Playwright Test version and be aware that long pages can make baselines more sensitive to dynamic content.

4. Handle native JavaScript dialogs separately

A consent banner is usually HTML content rendered in the page. It is not the same as a browser-native JavaScript alert, confirm, or prompt. A native dialog is handled through Playwright’s dialog event, not by locating and clicking an HTML button. When no dialog listener is installed, Playwright automatically dismisses JavaScript dialogs. If a listener is installed, it must accept or dismiss the dialog or page actions can stall. [Playwright Page API]

// Only use this when the page actually opens a native JavaScript dialog.
page.on('dialog', async dialog => {
  console.log(`Dialog type: ${dialog.type()}, message: ${dialog.message()}`);
  await dialog.dismiss();
});

Choose accept() or dismiss() according to the test behavior. A native dialog is not usually a cookie-consent interface, and the event handler should not be added as a substitute for finding the page’s actual consent controls. Selenium provides a separate WebDriver API for native alerts, prompts, and confirmations as well. [Selenium alert documentation]

5. Choose screenshot scope and visual treatments

Need Approach Trade-off
Visible search results page.screenshot({ path: 'serp.png' }) Captures the viewport; concise and usually best for a SERP view.
Everything below the fold page.screenshot({ path: 'serp.png', fullPage: true }) Produces a taller image; lazy-loaded content and page length can affect capture time and output size.
Hide a known irrelevant element in an approved test Use Playwright screenshot masks or screenshot-only styles. Changes the image appearance. It does not prove that consent was dismissed or that consent state changed.

Playwright supports full-page capture, masks, and screenshot-only styles. Use masking or hiding only when the capture requirements allow visual treatment. If the purpose is to show the page after a genuine consent choice, perform and verify that interaction instead. [Playwright Page API]

6. Troubleshooting

Symptom Likely cause Fix
Consent button wait times out The accessible name differs, the banner did not appear in this locale/session, or it is inside an iframe. Inspect the actual page state, use its visible label, and locate the correct frame if needed. Do not assume the example label is universal.
Click times out or reports an obstruction Another overlay covers the control, the control moved, or the page is still transitioning. Wait for the obstruction to become visible and handle it explicitly; then re-resolve and click the intended consent control.
Click succeeds but the banner remains The click targeted a different control, preferences need a second confirmation, or the banner has a nested panel. Check which choice was made and wait for the actual banner or panel to become hidden. Handle each step in the consent flow.
Screenshot still contains the banner The screenshot ran before the dismissal completed, or the consent UI is a different element than the locator checked. Wait for the banner itself to become hidden and, if appropriate, assert that the results content is visible before capturing.
Page action hangs after adding a dialog listener A JavaScript dialog listener was registered but did not accept or dismiss the dialog. Resolve every native dialog in the handler. Do not use a dialog handler for an HTML banner.
Unexpectedly different screenshots Viewport, browser version, locale, consent storage, live SERP content, fonts, or rendering environment changed. Fix the environment and initial state; compare stable page landmarks and account for inherently changing results.
Full-page image misses content or takes longer Content may be lazy-loaded, the page may be long, or full-page capture requires rendering beyond the initial viewport. Use viewport capture if that answers the task. For full-page output, wait for relevant content to load and assess whether scrolling or page-specific waits are needed.

7. Performance, reliability, and cost

For one-off captures, a single browser launch and one navigation are straightforward. In a repeated capture service, reusing a browser process while creating a fresh context per job can reduce launch overhead while keeping consent storage isolated. Set explicit navigation and locator timeouts, close contexts and browsers in cleanup paths, and record which consent action was selected so failed or unexpected captures can be diagnosed.

There is no universal reliable delay for all SERPs. A fixed sleep can waste time on fast pages and still be too short on slow ones. Prefer explicit visibility and hidden-state waits, plus a page-specific readiness check where available. Keep viewport dimensions consistent for comparable images. Full-page screenshots and high device scale factors increase image dimensions and may increase memory, processing time, and file size.

Self-hosted browser automation has infrastructure costs: browser CPU and memory, runtime, storage, and maintenance of selectors as consent interfaces change. A hosted screenshot API can remove browser setup from the workflow, but check its billing rules and how it treats consent UI before relying on it. ScreenshotNeo states that failed loads, timeouts, blank pages, bot checks/CAPTCHAs, and cache hits cost nothing, and each response identifies the page verdict and billing status in headers.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. A single GET request returns an image or PDF; its clean-capture flow accepts consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture. Each step can be turned off. It also supports custom browser options, including viewport/device presets, full-page capture, waits, cookies, headers, and caching. See the ScreenshotNeo API documentation for parameters and setup.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.google.com/search?q=playwright -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://www.google.com/search?q=playwright",
    },
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://www.google.com/search?q=playwright',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Only when the scenario calls for it. Start with fresh storage to exercise the first-visit dialog; reuse state when testing a returning visitor.

Can a screenshot hide the banner without clicking it?

Visual masks or screenshot-only styles can alter the image, but they do not perform a consent choice. Use them only when masking is allowed by the capture requirements.

Why does the SERP image change even when the script does not?

Search results and page content are live and can change. Stable browser settings reduce rendering variation but cannot freeze the underlying SERP.

References