ScreenshotNeo

BlogHow-to

Capture Screenshots of Multiple Logged-In Pages with Selenium in Python

Use one authenticated Selenium session to capture multiple pages in Python, with explicit waits, safe credential handling, troubleshooting, and a ScreenshotNeo alternative.

By the ScreenshotNeo team4 October 20269 min read

To capture screenshots of multiple logged-in pages with Selenium in Python, sign in once, keep the same WebDriver session open, then navigate to each page, wait for its authenticated content to appear, and save a uniquely named screenshot. A single session lets the browser retain its authentication context across navigation. A successful navigation alone does not guarantee that JavaScript-rendered content is ready, so use explicit, page-specific waits.

The example below is a runnable template, but the login fields, selectors, and readiness conditions must match the site you are authorized to access. It captures the current browser window; it does not promise a full-page image.

1. Install Selenium and prepare the browser

Use a supported browser installed on your machine. Selenium’s current setup flow can manage drivers through Selenium Manager. Install the Python package in the environment where the script will run:

python -m pip install selenium

Save the following as capture_pages.py. Set LOGIN_URL, the target URLs, and selectors for the site. Provide credentials through environment variables rather than embedding them in the file.

2. Sign in once, wait for each page, and save screenshots

import os
import re
from pathlib import Path
from urllib.parse import urlparse

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
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.com/login"
PAGES = [
    ("account", "https://example.com/account", (By.CSS_SELECTOR, "h1.account-title")),
    ("invoices", "https://example.com/account/invoices", (By.CSS_SELECTOR, "table.invoice-list")),
    ("settings", "https://example.com/account/settings", (By.CSS_SELECTOR, "form.settings-form")),
]
OUTPUT_DIR = Path("screenshots")
WAIT_SECONDS = 20

username = os.environ.get("SITE_USERNAME")
password = os.environ.get("SITE_PASSWORD")
if not username or not password:
    raise RuntimeError("Set SITE_USERNAME and SITE_PASSWORD in the environment.")

# Select the browser and options appropriate for your environment.
options = webdriver.ChromeOptions()
# Uncomment for a headless run. Headless screenshots can differ from headed output.
# options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, WAIT_SECONDS)

try:
    driver.get(LOGIN_URL)

    # Replace selectors with those used by the authorized login page.
    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()

    # Confirm authentication using a reliable site-specific indicator.
    wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='account-menu']")))

    for name, url, ready_locator in PAGES:
        driver.get(url)
        try:
            wait.until(EC.visibility_of_element_located(ready_locator))
        except TimeoutException as exc:
            raise TimeoutException(
                f"Timed out waiting for {ready_locator} on {url}"
            ) from exc

        # Use a controlled name rather than a URL as a filesystem path.
        safe_name = re.sub(r"[^A-Za-z0-9_-]+", "-", name).strip("-") or "page"
        output_path = OUTPUT_DIR / f"{safe_name}.png"
        if not driver.save_screenshot(str(output_path)):
            raise RuntimeError(f"WebDriver could not save {output_path}")
        print(f"Saved {output_path} ({urlparse(url).netloc})")
finally:
    driver.quit()

Run it after setting the environment variables. For example, in a POSIX shell:

export SITE_USERNAME='your-account-name'
export SITE_PASSWORD='your-secret'
python capture_pages.py

On Windows PowerShell, set them for the current session with $env:SITE_USERNAME='your-account-name' and $env:SITE_PASSWORD='your-secret', then run python capture_pages.py. Use your organization’s approved secret store or CI secret mechanism for scheduled jobs.

3. Choose a readiness condition that matches the page

Selenium navigation normally waits for the document readiness state to reach complete. That state does not mean a single-page app has finished fetching data or that an animation, image, or client-rendered component has settled. Choose a condition tied to the content the screenshot must include. Selenium documents explicit waits and expected conditions for this purpose: Waiting strategies.

  • Element visible: use visibility_of_element_located when the element must be displayed.
  • Element present: use presence_of_element_located if presence in the DOM is enough, even if it is hidden.
  • Text available: use text_to_be_present_in_element when a loading placeholder is replaced with known text.
  • URL or title changed: use URL or title conditions after a redirect or client-side route transition.
  • Custom state: use WebDriverWait(...).until(lambda d: ...) for a site-specific condition, such as a loading indicator disappearing.

Keep waits finite. A timeout should tell you which page and condition failed, instead of silently saving an incomplete image. Avoid mixing implicit and explicit waits because Selenium warns that their combined timing can be unpredictable; use explicit waits consistently for this workflow.

4. Login and session edge cases

Redirects and single-page applications

A successful login may redirect to a landing page, update the page without a full navigation, or take several seconds to load account data. Wait for a distinctive authenticated element rather than assuming the submit click means login succeeded. Before capturing each target, wait for a marker specific to that page. If the URL redirects back to login, treat that as an authentication failure and do not save it as a successful capture.

Cookies and reused sessions

The simplest reliable approach is usually to complete the site’s normal login flow in the same driver that visits the target pages. Selenium has cookie APIs, but adding a cookie requires first being on a domain where that cookie is valid, and authentication can depend on more than one cookie. A copied cookie by itself is not a universal way to create a valid session. See Selenium’s cookie documentation.

If your approved workflow reuses cookies, keep the browser on the relevant domain before adding them, and follow the site’s security requirements. Do not store session cookies in source control or logs. If a session expires during a long run, detect the login page or lost authenticated marker, then stop or use the site’s permitted reauthentication flow.

MFA, CAPTCHA, and access controls

Some sites require multi-factor authentication, interactive approval, or present anti-automation checks. This script does not bypass them. Follow the site’s permitted automation path; if the site requires human interaction, complete it through the authorized process or request an approved testing account or integration.

5. Screenshot scope and output options

driver.save_screenshot(path) saves a screenshot of the current window. Its result is a boolean indicating whether the file was saved. Selenium also provides get_screenshot_as_file(path) and get_screenshot_as_png() for file and in-memory workflows. See the Python WebDriver API.

  • Viewport size: set the window size before capture for repeatable visible-area dimensions. Browser chrome and operating system differences can still affect the content area.
  • Full-page output: the standard screenshot call captures the current window. Do not assume it stitches the entire document. Full-page techniques vary by browser and implementation; validate the exact method and output you choose.
  • File names: use a stable page identifier, as in the example, and ensure names are unique. Avoid using raw URLs, which can contain unsafe path characters or sensitive query values.
  • File format: WebDriver’s standard screenshot methods produce PNG data. Convert or post-process only if another format is required.
  • In-memory processing: get_screenshot_as_png() returns bytes you can pass to a storage client or image library instead of writing locally.

6. Reliability, performance, and cost

One WebDriver session avoids repeating login for every page and keeps navigation and capture in a clear sequence. For a small set of pages, sequential captures are easy to debug and reduce simultaneous load on the site. If you parallelize work, use separate browser sessions and respect the site’s rate limits and automation policy; one WebDriver instance should not be driven concurrently by multiple workers.

Set explicit timeouts for page conditions, keep the list of pages deterministic, and fail visibly when a required element is absent. A screenshot can still be visually wrong if the selected marker appears before the specific content settles, if a banner covers the content, or if the browser uses a different viewport than expected. Choose a readiness signal that represents the actual capture requirement.

Selenium itself is open-source browser automation software; operational costs depend on where the browser runs, the machine or hosted execution environment, storage, and any service you use. This workflow makes no claim about a particular runtime or price. For private pages, consider where screenshots and browser profiles are stored, how long they are retained, and who can access them.

7. Troubleshooting

Symptom Likely cause Fix
Login fields time out Selectors do not match the page, or the login form is inside a frame or appears later. Inspect the authorized page structure, update selectors, and wait for the correct frame or field.
Every target redirects to login Authentication did not complete, the session expired, or the target requires another permission. Wait for a signed-in indicator after login; verify the account has access and use the permitted sign-in flow.
Screenshot is blank or missing dynamic content Navigation completed before client-rendered content was ready, or the readiness selector is too early. Wait for the actual content or a stable page-specific signal; do not rely only on document readiness.
Wait times out on a page The selector is wrong, the page changed, access was denied, or the page is genuinely slow. Check the current URL and page state, verify the selector, and set a finite timeout appropriate for the workflow.
Some captures overwrite others Two entries map to the same output filename. Assign distinct stable names and check for duplicates before the run.
Cookie insertion fails The current browser page is not on a domain valid for that cookie, or its attributes do not match. Navigate to the relevant domain first and follow Selenium’s cookie guidance; prefer normal login when feasible.
Browser does not start Browser installation, driver setup, permissions, or headless configuration is incompatible with the environment. Confirm the browser is installed and supported, review Selenium setup guidance, and try a headed run to inspect startup errors.
Screenshot differs between runs Viewport, responsive layout, asynchronous content, animation, or session state varies. Fix the window size, wait for stable content, and use a consistent account and environment.

8. Or skip the browser setup

For public pages, ScreenshotNeo can capture a URL with one GET request. Its API does not use your Selenium login session, so it is not a substitute for capturing private pages that require your authenticated browser. See the ScreenshotNeo API documentation for parameters and options.

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)
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}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 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 I capture pages from different subdomains after one login?

Only if the site’s authentication is valid across those subdomains and the account is authorized for each page. Verify the signed-in state on each destination.

Does this capture the whole page?

No. The example saves the current browser window. Full-page capture needs a separately selected and validated technique.

Can I use the same script for any website?

The navigation loop is reusable, but login fields, account indicators, page selectors, and access policies are site-specific.

Can I screenshot pages that require a password with ScreenshotNeo?

The API example captures a URL without reusing the Selenium browser session. Use the Selenium workflow for pages that depend on your authenticated session.

Official Selenium references