ScreenshotNeo

BlogHow-to

Screenshot a Web App That Requires a Login CAPTCHA with Selenium

Capture an authorized login page or CAPTCHA with Selenium, wait for the right state, and handle human verification safely. Includes Python, JavaScript, Java, and ScreenshotNeo examples.

By the ScreenshotNeo team4 October 20269 min read

Use Selenium WebDriver to open the login page, wait until the state you want to document is visible, and save a browser screenshot. If you need only the CAPTCHA widget, save a screenshot of that element. Selenium can capture what the browser displays; it does not make CAPTCHA verification an automated step. For an authorized test, capture the challenge as shown or have a human complete it, or ask the site owner for a test environment or test-only CAPTCHA configuration.

This guide is for pages and accounts you are authorized to test. It does not cover solving or bypassing CAPTCHA. Selenium describes WebDriver as an interface for browser instructions that can work across browsers. Selenium WebDriver documentation.

1. Choose what state to capture

First decide what the screenshot should prove. A viewport screenshot records the visible login page as a whole. An element screenshot focuses on the CAPTCHA widget or another specific region. These answer different debugging questions, so capture both when the overall page context matters as well as the widget details.

Capture Use it for Limitation
Browser viewport Login layout, error messages, challenge placement, and current visible state Only the current browser view is guaranteed; content below the fold may not appear
Target element A CAPTCHA widget, login form, or other specific component Can fail if the element is hidden, detached, covered, or outside a screenshot-capable context

Identify the CAPTCHA type the application actually uses. Google documents v2 checkbox and challenge behavior, and v3 score-based assessment without a visible user interaction. A v3 integration may not show a checkbox at all. Do not assume that every site uses the same version or that a successful interaction remains valid indefinitely. Google reCAPTCHA versions.

2. Capture the login page using Selenium Python

The following example opens an authorized login URL, waits for a stable page landmark, saves the visible viewport, and optionally saves a CAPTCHA element if one is present. It does not submit credentials or interact with the challenge. Install Selenium with python -m pip install selenium; Selenium Manager can manage supported browser drivers when the browser is installed.

import os
from pathlib import Path

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

LOGIN_URL = os.environ.get("LOGIN_URL", "https://example.com/login")
OUT = Path("screenshots")
OUT.mkdir(exist_ok=True)

options = webdriver.ChromeOptions()
# Uncomment for a headless run. Keep the viewport explicit for consistent layout.
# options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get(LOGIN_URL)
    wait = WebDriverWait(driver, 20)

    # Replace this selector with a stable landmark from the authorized app.
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "form")))
    driver.save_screenshot(str(OUT / "login-viewport.png"))

    # Replace with the site's actual widget/container selector.
    captcha_selector = "[data-testid='captcha']"
    try:
        captcha = wait.until(
            EC.visibility_of_element_located((By.CSS_SELECTOR, captcha_selector))
        )
        captcha.screenshot(str(OUT / "captcha-element.png"))
    except TimeoutException:
        print("CAPTCHA element was not visible; viewport screenshot was saved.")
finally:
    driver.quit()

The element screenshot methods and return values vary by binding and version. The Selenium Python WebElement API documents saving an element screenshot as PNG, plus screenshot data as bytes or base64. Check the documentation for the installed version before relying on a particular method. Selenium Python WebElement API.

Python details and alternatives

  • driver.save_screenshot(path) writes the current browser view as a PNG and returns a success value in Selenium Python. Check that value or confirm the file exists when the screenshot is an artifact your test must retain.
  • element.screenshot(path) captures a particular element. Use a stable selector owned by the application, such as a test ID, instead of a fragile generated class.
  • For bytes in memory, use the binding’s documented screenshot-bytes property and write those bytes to a file or artifact store. Avoid converting through base64 unless an API requires it.
  • Do not save passwords, session cookies, or unrelated sensitive account data in screenshot artifacts. Use a dedicated test account and restrict artifact access.

3. Other Selenium bindings

The same sequence applies in other bindings: navigate, wait for a meaningful element, capture the viewport, and optionally capture a target element. Confirm method names against the Selenium version in use.

JavaScript (Node.js)

Install the binding with npm install selenium-webdriver and have Chrome installed. This example uses Chrome and writes PNG files.

const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const fs = require('node:fs/promises');

(async () => {
  const options = new chrome.Options().windowSize({ width: 1440, height: 1000 });
  // For headless capture, add: options.addArguments('--headless=new');
  const driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build();
  try {
    await driver.get(process.env.LOGIN_URL || 'https://example.com/login');
    const form = await driver.wait(until.elementLocated(By.css('form')), 20000);
    await driver.wait(until.elementIsVisible(form), 10000);
    await fs.writeFile('login-viewport.png', await driver.takeScreenshot());

    const captcha = await driver.findElement(By.css("[data-testid='captcha']"));
    await driver.wait(until.elementIsVisible(captcha), 10000);
    await fs.writeFile('captcha-element.png', await captcha.takeScreenshot());
  } finally {
    await driver.quit();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

If the CAPTCHA selector is optional, handle the element-not-found or wait timeout as an expected condition and preserve the viewport screenshot. Do not swallow browser startup, navigation, or file-writing failures.

Java

With Selenium Java on the project classpath and a configured Chrome browser, the viewport capture uses TakesScreenshot. The WebDriver element screenshot API depends on binding and version; verify it in the API documentation if you also need an element-only image.

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;

import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class LoginScreenshot {
    public static void main(String[] args) throws Exception {
        String url = System.getenv().getOrDefault("LOGIN_URL", "https://example.com/login");
        WebDriver driver = new ChromeDriver();
        try {
            driver.manage().window().setSize(new org.openqa.selenium.Dimension(1440, 1000));
            driver.get(url);
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
            wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("form")));
            File temp = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            Files.copy(temp.toPath(), Path.of("login-viewport.png"));
        } finally {
            driver.quit();
        }
    }
}

4. Handle the CAPTCHA as a human verification step

A CAPTCHA exists to help a site distinguish people from automated traffic. Google says a user may be asked to complete an on-screen challenge. If the challenge blocks the login flow, a repeatable test should stop at that state, save the challenge screenshot, and report that human verification is required. If the test specifically needs the authenticated page, arrange an approved human-controlled step or a test-only configuration with the application owner. Google also notes that reCAPTCHA verification may expire, so an earlier pass should not be treated as permanent. Google reCAPTCHA overview and Google reCAPTCHA help.

  1. Run against a test account and environment you are allowed to access.
  2. Wait for the expected login or challenge state rather than sleeping for an arbitrary long interval.
  3. Save the viewport or challenge element for the current state.
  4. If the workflow requires continuing, pause for an authorized person or use the site’s documented test configuration.
  5. Record whether the challenge appeared and whether a human completed it; do not report a blocked login as a successful authenticated test.

5. Wait for the right page state

Navigation returning does not prove that client-side rendering, fonts, images, or the CAPTCHA frame have settled. Prefer an explicit wait for an application landmark, visible challenge container, or a known post-verification state. Selenium’s explicit wait checks for a condition until it succeeds or times out; it is more robust than an arbitrary fixed delay. A frame-based widget may need special handling to inspect its internals, and a cross-origin frame can restrict what the page exposes. For a visual record, the top-level viewport screenshot may still show the rendered challenge.

  • Wait for presence when the element only needs to exist in the DOM; wait for visibility when it must appear in the screenshot.
  • Wait for a loading overlay to disappear if it obscures the page.
  • For a post-verification capture, wait for a specific application state, not merely the disappearance of the challenge.
  • Use a fixed delay only for a known animation or timing requirement, and keep it short and documented.

6. Troubleshooting

Symptom Likely cause Fix
Screenshot is blank or shows an old page The page had not reached the intended state, navigation failed, or the wrong window/tab is active Check the current URL and browser logs; wait for a visible page landmark before capturing
Element lookup times out The selector is wrong, the widget did not load, or it is inside a frame Inspect the authorized page DOM, use a stable container selector, wait for visibility, and switch to the relevant frame only when appropriate
Element screenshot fails The node is detached, hidden, outside the supported context, or changes during capture Re-find it after the page settles; ensure it is visible; capture the viewport if the element API cannot capture that context
CAPTCHA appears inconsistently Challenge presentation depends on the site integration and verification decision Make the test accept both expected states and report which appeared; coordinate a test configuration with the site owner for deterministic tests
Login never reaches the authenticated page The CAPTCHA requires human verification or verification expired Pause for an authorized person or use the documented test setup; do not treat a previous verification as reusable
Browser fails to start Browser is missing, driver/browser versions are incompatible, or CI lacks required libraries Install a supported browser, check Selenium and browser versions, and review the driver startup error
Screenshot dimensions differ in CI Viewport, device scale factor, fonts, or browser environment differs Set a consistent window size, use a stable browser image and fonts, and compare at the same scale
Output file is missing or empty Path does not exist, permissions are wrong, or the screenshot result was not checked Create the output directory, use an absolute/known path, and verify the file after capture

7. Reliability, performance, and cost

Browser startup and page rendering usually dominate screenshot time; a single screenshot call is only one part of the workflow. Reusing a WebDriver session across related captures can avoid repeated startup, but isolate sessions when cookies or account state could leak between tests. Always quit the driver in a finally block so failed waits do not leave browser processes behind.

For reliable comparisons, keep browser version, viewport, device scale, fonts, locale, and test data consistent. Prefer a test environment and stable test selectors. CAPTCHA challenge appearance can vary, so tests should classify the observed state instead of asserting that every run follows one path. Selenium itself is open-source browser automation; execution cost comes from the machines and browser infrastructure you run, plus any application or CAPTCHA service charges governed by their own terms. No universal timing or cost figure applies across sites and CI environments.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. A single GET request captures a URL as PNG, JPEG, WebP, or PDF. This is useful for public pages; it does not replace an authorized login session or complete a CAPTCHA. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
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(({ writeFile }) => 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 screenshots. Create a free ScreenshotNeo account.

9. FAQ

Can Selenium take a screenshot while the CAPTCHA is visible?

Yes. It can capture the visible browser viewport or a visible target element. Capturing the challenge is different from completing it.

Will a screenshot include a CAPTCHA inside an iframe?

A viewport screenshot records the rendered browser view, including visible frame content in normal cases. An element screenshot or DOM inspection inside a cross-origin frame may be limited; capture the outer visible page if needed.

Does reCAPTCHA always show a checkbox?

No. The site may use different reCAPTCHA versions and integrations. Google documents a checkbox/challenge flow for v2 and a score-based flow for v3.

Can a Selenium screenshot capture a page behind a login?

Only if the browser is already in an authorized authenticated state. A screenshot API that fetches a public URL is not a substitute for your logged-in browser session.