ScreenshotNeo

BlogHow-to

How to Take a Screenshot of a Webpage That Requires Login with Selenium

Log in with Selenium, wait for proof the protected page is ready, and save a reliable screenshot with Python examples and troubleshooting.

By the ScreenshotNeo team4 October 202610 min read

To screenshot a page that requires login with Selenium, start a WebDriver session, authenticate with an account you are authorized to use, open the protected URL, wait for a page-specific signal that proves both authentication and rendering succeeded, and save the screenshot. Selenium’s Python binding can save the current browser view as a PNG with driver.save_screenshot().

For repeatable test setup, Selenium recommends establishing application state through another supported route when possible, such as using an application API to log in and then installing a valid session cookie. If the test needs to cover the login experience itself, use the site’s normal login flow. Cookie names, values, scope, expiry, and any additional server-side state belong to the application; there is no universal login cookie recipe. Selenium: Generating application state.

1. Install Selenium and choose an authentication path

Install the Python binding:

python -m pip install selenium

Recent Selenium versions can manage supported browser drivers through Selenium Manager. You still need a compatible browser installed. In managed CI environments, follow the environment’s browser and driver setup instructions.

Choose the authentication approach that matches what you are trying to verify:

Approach Use it when Tradeoff
Normal login flow You are testing login, MFA, redirects, or the actual user journey. More realistic coverage, but slower and more sensitive to changes in the login interface.
Application API plus session cookie The screenshot task starts after login and the application supports a stable, authorized setup API. Often simpler for repeatable test preparation, but it does not test the login UI and may require other session state.
Existing authorized browser profile A controlled local workflow intentionally reuses a profile. Can carry stale or unrelated state; avoid sharing the profile across parallel jobs.

Use a dedicated test account and test data where possible. Do not put passwords, access tokens, or session cookies in source control, logs, screenshots, or shared browser profiles.

2. Log in, verify the protected page, and save a screenshot

This complete example uses placeholders for the site’s login fields, URLs, and authenticated-page selector. Replace them with locators and conditions that are true for your application. The code uses an explicit wait and saves the current viewport as protected-page.png.

import os
from pathlib import Path

from selenium import webdriver
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 = "https://example.test/login"
PROTECTED_URL = "https://example.test/account"
READY_SELECTOR = "[data-testid='account-home']"  # Replace with a unique, authenticated-only element

username = os.environ["TEST_USERNAME"]
password = os.environ["TEST_PASSWORD"]

options = webdriver.ChromeOptions()
# In a controlled CI/container environment, headless mode may be appropriate:
# options.add_argument("--headless")

# Set a viewport that matches the screenshot you need.
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 20)

try:
    driver.get(LOGIN_URL)

    wait.until(EC.visibility_of_element_located((By.NAME, "username"))).send_keys(username)
    driver.find_element(By.NAME, "password").send_keys(password)
    driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()

    # Wait for an authenticated-only condition, not just a successful click.
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, READY_SELECTOR)))

    driver.get(PROTECTED_URL)
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, READY_SELECTOR)))

    # Optional: check that a login form is absent after navigation.
    if driver.find_elements(By.CSS_SELECTOR, "form[data-testid='login-form']"):
        raise RuntimeError("The protected page redirected to a login form")

    output = Path("protected-page.png")
    if not driver.save_screenshot(str(output)):
        raise RuntimeError("WebDriver reported that screenshot saving failed")
    print(f"Saved {output.resolve()}")
finally:
    driver.quit()

The example’s selectors are illustrative, not universal. Use a stable marker such as an account navigation element, a page heading, or a test-specific attribute that appears only after authentication. If your app transitions asynchronously, wait for the final content you need too—for example, a table row, chart, or loaded-state marker.

Selenium’s waiting guidance explains why navigation returning is not enough: WebDriver waits for a document readyState based on its page-load strategy, but JavaScript can continue to update the page afterward. Use an explicit wait for the condition the screenshot depends on. Avoid mixing implicit and explicit waits because Selenium warns that this can produce unpredictable wait durations.

If the application provides an authorized test or login API, it may return a session cookie that lets the browser begin from an authenticated state. Cookie setup is application-specific, so adapt the cookie fields and handling to the service’s documentation. WebDriver requires the current browsing context to be on a page within the cookie’s valid domain before adding that cookie.

import os
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

BASE_URL = "https://example.test"
PROTECTED_URL = f"{BASE_URL}/account"
READY_SELECTOR = "[data-testid='account-home']"

# Obtain this through your application's approved test/auth API.
# Keep it secret and do not print it.
session_id = os.environ["TEST_SESSION_ID"]

options = webdriver.ChromeOptions()
options.add_argument("--window-size=1440,1000")
driver = webdriver.Chrome(options=options)

try:
    # First load a URL on the cookie's valid domain.
    driver.get(BASE_URL)
    driver.add_cookie({
        "name": "sessionid",       # Replace with the real cookie name
        "value": session_id,        # Replace with the API-issued value
        "path": "/",
        "secure": True,
        "httpOnly": True,
        # Set "sameSite" only to a value supported by the browser and app.
        # Set "expiry" only if the API provides a suitable Unix timestamp.
    })

    driver.get(PROTECTED_URL)
    WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, READY_SELECTOR))
    )
    driver.save_screenshot("protected-page.png")
finally:
    driver.quit()

This pattern works only if the cookie is valid for the requested host and path and is sufficient to establish the session. Some applications also require CSRF state, local storage, a second cookie, device binding, or a server-side session that has expired. Follow the application’s supported test setup rather than copying a browser cookie from a real user session.

4. Choose what part of the page to capture

Current browser viewport

driver.save_screenshot("page.png") captures the current browser window’s visible area. Set the window size before navigation when you need a consistent viewport. The output is a PNG.

One element

For a focused component, locate it after it is visible and use the element screenshot method:

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

card = WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='billing-card']"))
)
card.screenshot("billing-card.png")

An element screenshot captures the visible region encompassed by that element according to the browser/driver behavior. If the element extends beyond the viewport, scroll it into view and verify the resulting capture in your chosen browser. Selenium documents page- and element-level screenshot operations in its WebDriver browser documentation.

Entire scrollable document

A normal page screenshot is not automatically a full-document screenshot. Full-page capture support depends on the browser, driver, and Selenium binding/version. Confirm the exact support for your environment before relying on it. If unavailable, possible approaches include a browser-specific full-page capture facility or stitching multiple viewport captures; stitching needs care around sticky headers, lazy-loaded content, animations, and overlapping regions.

5. Make the capture repeatable

  • Use a meaningful readiness condition. Wait for an authenticated-only element and, where needed, for the specific content to finish rendering.
  • Set the viewport deliberately. Viewport dimensions affect responsive layout, line wrapping, and what fits in the screenshot.
  • Control test data. A stable account and known data state make visual differences easier to interpret.
  • Account for lazy content. Scroll the relevant content into view and wait for images or components that load on demand.
  • Disable or wait out animation when appropriate. For visual checks, application-supported reduced-motion settings or targeted test CSS can prevent mid-animation captures.
  • Keep sessions isolated. Use separate test accounts or browser profiles for parallel work when session state could conflict.
  • Protect artifacts. Screenshots of account pages can contain personal, financial, or otherwise private data. Restrict storage and retention.

WebDriver page-load strategies can change how long navigation blocks: the default waits for the document’s complete state; eager returns when the DOM is interactive while some assets may still load; none does not block on page loading. Changing that setting does not remove the need to wait for application-specific readiness. See Selenium browser options.

6. Troubleshoot common failures

Symptom Likely cause Fix
The screenshot shows the login page. Credentials were rejected, the login is asynchronous, the session expired, or the protected URL redirected. Wait for a success marker after login; inspect the final URL and page title; explicitly detect the login form; verify the account and session are valid.
TimeoutException waiting for the page marker. The selector is wrong, the marker never becomes visible, or the page did not authenticate/render. Check the selector in the actual page, confirm the marker is unique and visible, and inspect the current URL and a diagnostic screenshot on failure.
NoSuchElementException for a login field. The page has not rendered the form yet, the locator differs, or the form is inside an iframe. Wait for the field, validate the locator, and switch to the relevant frame before locating elements inside it.
Cookie addition fails or has no effect. The browser is not on the cookie’s domain, or the domain/path/security attributes do not match. Navigate to the cookie’s valid host first; use the attributes issued by the application; confirm HTTPS and expiry; add all required auth state.
The page loads but the screenshot is blank or incomplete. JavaScript rendering, fonts, images, or application data are still loading; a bot check or error page may have appeared. Wait for a task-specific content marker, check for error/verification pages, and wait for relevant images or data rather than sleeping for an arbitrary fixed duration.
ElementClickInterceptedException or click does nothing. An overlay, animation, or cookie banner covers the control, or the element is not interactable. Wait for the overlay to disappear or handle it as the site requires; wait for clickability and inspect whether the click caused a navigation or state change.
Driver startup or session creation fails. Browser/driver incompatibility, missing browser, or environment restrictions. Confirm the browser is installed and supported, update Selenium and the browser/driver setup together, and review the specific browser logs.
Screenshot file is missing or invalid. The target directory is wrong/unwritable or the save operation returned false. Use an explicit output path, ensure its parent exists and is writable, check the return value, and validate the artifact before downstream use.

Selenium identifies synchronization problems as a common source of WebDriver failures. Prefer waiting for the state you need over adding a long fixed sleep. For error-specific guidance, see Understanding common errors.

7. Performance, reliability, and cost

A browser screenshot requires starting or reusing a browser session, authenticating, loading the target page, waiting for its meaningful ready state, and writing an image. The biggest avoidable delay in repeated test preparation is often replaying the login UI when the goal is only to reach an authenticated starting state; Selenium’s guidance recommends using another supported setup method such as an API and cookie when suitable. Do not shorten waits by guessing: wait for the condition needed by the capture.

For reliability, keep each capture isolated, use explicit waits, treat login redirects and verification pages as failures, and preserve useful diagnostics without recording secrets. Selenium and browser execution have no ScreenshotNeo per-image price; infrastructure cost depends on where and how you run the browser, and the dossier provides no benchmark figures.

8. Or skip the browser setup

If you need a screenshot of a publicly reachable page, ScreenshotNeo is a website screenshot API and MCP server. It does not log in to private accounts, so use Selenium for pages that require your authenticated browser session. For public pages, one GET request returns an image or PDF; 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()
open("shot.webp", "wb").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 Bun.write("shot.webp", res);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its 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. Sign up for free and get 1,000 screenshots a month with no card.

9. Frequently asked questions

Can Selenium take a screenshot after logging in with MFA?

It can capture the resulting page after authentication, but the MFA flow depends on the site and your authorized test setup. Use a test-friendly authentication path approved by the application owner; do not bypass controls on accounts you do not own.

Can I save the screenshot as JPEG?

The Selenium screenshot methods shown here save PNG. Convert the resulting image with an image library if your downstream workflow requires JPEG or another format.

Will headless Chrome produce the same screenshot as a visible browser?

Do not assume pixel identity. Browser version, fonts, viewport, device scale, rendering environment, and timing can affect output. Keep those inputs consistent for comparisons.

Can I use a screenshot of a private page with ScreenshotNeo?

This article’s ScreenshotNeo example is for publicly reachable pages. For a protected page that needs an authenticated session, use the authorized Selenium workflow above.