ScreenshotNeo

BlogHow-to

How to Fix Blank Website Screenshots in Selenium

Selenium can finish navigation before a JavaScript page is ready to capture. Diagnose blank screenshots with page-specific waits, lazy-load checks, and practical troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

A Selenium screenshot can be blank even after navigation returns because the browser’s document load state does not guarantee that a JavaScript application has rendered the content you want. Wait for a page-specific element to become visible, check that the browser reached the expected page, and only then save the screenshot.

This guide uses Python with Selenium. The same rule applies in other Selenium language bindings: synchronize on the content you intend to capture, rather than assuming that a navigation command means the page is visually ready.

1. Confirm the browser reached the expected page

Before changing waits, make sure the browser is on the page you meant to capture. A redirect, login wall, browser error page, or failed navigation can look like a blank screenshot or an empty application.

print("URL:", driver.current_url)
print("Title:", driver.title)
print("Ready state:", driver.execute_script("return document.readyState"))
print("Body text:", driver.find_element(By.TAG_NAME, "body").text[:500])

Compare the current URL and title with the expected destination. If the page is an error or sign-in page, fix that navigation or authentication issue before investigating screenshot timing.

2. Wait for the content you actually need

Selenium’s default page-load strategy waits for the document’s readyState to reach complete. That covers assets defined in the HTML, but JavaScript can still update the page afterward. A single-page application may return from navigation before its main content appears.

Use an explicit wait for a reliable page-specific element. The example below waits until the main article is visible and then captures the current browser window as a PNG.

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

url = "https://example.com"
main_selector = "main"  # Replace with a selector that identifies the content to capture.

options = webdriver.ChromeOptions()
# Uncomment to run without a visible browser window:
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.set_window_size(1440, 1000)
    driver.get(url)

    WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, main_selector))
    )

    driver.save_screenshot("page.png")
finally:
    driver.quit()

Replace main with a selector for the specific rendered content that matters, such as a chart, product details panel, or article container. A generic element can exist before the page is useful, so choose a condition that reflects the intended capture.

Explicit waits poll for a condition until it succeeds or times out. If the condition never becomes true, Selenium raises a timeout error instead of silently capturing too early. Do not mix implicit and explicit waits; Selenium warns that their combined timing can be unpredictable. A fixed sleep can be useful as a temporary diagnostic, but it is a poor default: too short still races, while too long adds avoidable delay.

3. Understand page-load strategies

Selenium supports three page-load strategies. Changing one can affect when navigation returns, but none guarantees that a dynamic page is ready for a screenshot. Keep a content-specific explicit wait after navigation when the target is rendered asynchronously.

Strategy Navigation waits until What it means for screenshots
normal (default) The document reaches complete. HTML-defined resources have completed loading, but client-side code may still change the page.
eager The document reaches interactive / DOMContentLoaded. The DOM can be accessed while images and other resources may still be loading.
none Navigation does not wait for document readiness. Your code takes responsibility for every readiness check before interacting or capturing.

For example, set a strategy in Chrome options when you have a reason to return from navigation earlier, then still wait for the target content:

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

options = webdriver.ChromeOptions()
options.page_load_strategy = "eager"  # "normal", "eager", or "none"

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
    )
    driver.save_screenshot("page.png")
finally:
    driver.quit()

4. Check lazy-loaded and viewport-triggered content

Some sites load images or sections only when they approach or enter the viewport. If the missing content is below the fold, scrolling it into view can help diagnose whether viewport visibility triggers loading. It is not a universal fix: wait for the specific image or section after scrolling, and check the site’s behavior.

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

image = driver.find_element(By.CSS_SELECTOR, "img.product-photo")
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", image)
WebDriverWait(driver, 15).until(
    lambda d: image.get_attribute("complete") == "true"
    and image.get_attribute("naturalWidth") not in (None, "0")
)
driver.save_screenshot("page.png")

Google’s guidance for lazy-loaded pages is to load relevant content whenever it is visible in the viewport. For a screenshot workflow, identify whether the site uses viewport-triggered loading, bring the relevant content into view if needed, and wait for that content to finish rendering. Google Search Central: Fix Lazy-Loaded Website Content.

5. Troubleshoot a blank or incomplete capture

Symptom Likely cause to investigate What to try
The screenshot is entirely blank, but navigation returned. The app renders its content after the document load event. Wait for a page-specific element to be visible; inspect the URL, title, and body text.
The expected element never appears. The selector is wrong, the page redirected, content requires authentication, or a page error stopped rendering. Check the current URL and title, inspect the DOM, and verify the selector against the actual page.
The page has a header but missing content lower down. Content may be lazy-loaded or triggered by viewport visibility. Scroll the relevant region into view, then wait for its image or content state. Do not assume scrolling fixes every site.
The wait times out intermittently. Load time varies, the condition is too strict, or the app sometimes fails to render. Use the most stable meaningful selector, set a reasonable timeout for the environment, and inspect browser and network errors when it still fails.
It works locally but fails in CI or headless mode. The browser, driver, runtime, viewport, or page behavior may differ between environments. Record browser and driver versions, viewport, headless setting, current URL, and console or network errors. Compare a visible run when practical.
The screenshot captures a loading spinner or skeleton. The selected wait condition is present before the real content is ready. Wait for a content-specific state, such as the data panel becoming visible or the loading indicator disappearing.
The PNG file is missing or empty. The capture may not have run, the process may have exited early, or the output path may not be writable. Check the return value from save_screenshot, use an explicit writable path, and ensure cleanup occurs after capture.

Blank output can also involve browser console errors, failed resources, redirects, authentication, or differences in a CI/container runtime. These are possibilities to investigate, not a diagnosis without the page, logs, and environment details.

6. Reliability, runtime, and cost considerations

  • Reliability: A condition tied to the content you need is more robust than a fixed delay. Keep the wait bounded so a page failure produces a useful timeout instead of a job that hangs indefinitely.
  • Runtime: normal may wait for more resources before your code proceeds; eager and none can return earlier, but require careful readiness checks. A long fixed sleep adds the same delay on fast and slow loads.
  • Capture scope: save_screenshot captures the current window. Set a deliberate viewport size for repeatable viewport screenshots. A full-page image may need a browser-specific approach; do not assume a window screenshot includes content outside the viewport.
  • Cost: Selenium itself is browser automation software; the operational cost of a self-managed capture comes from the machine, browser runtime, maintenance, and time spent handling failures. There is no benchmark or universal cost figure for a particular workload.

7. ScreenshotNeo option: capture without managing a browser

If the goal is a website screenshot rather than Selenium-specific interaction or test coverage, ScreenshotNeo provides a screenshot API and MCP server. Its API supports PNG, JPEG, WebP, and PDF captures; the ScreenshotNeo documentation covers request options.

Or skip the browser setup

Send one GET request with the target URL. This runnable cURL example saves a WebP screenshot:

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

Python:

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)

Node.js:

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 accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo free: 1,000 screenshots a month, no card required.

8. FAQ

Does document.readyState == "complete" mean the screenshot is ready?

No. It reports document loading state, not that a JavaScript application has finished rendering the specific content you want.

Should I use an implicit wait for screenshot readiness?

Prefer an explicit wait for the page-specific condition you need. Selenium advises against mixing implicit and explicit waits.

Will changing to eager fix a blank screenshot?

Not by itself. It changes when navigation returns; add a condition-based wait for the rendered content.

Can scrolling fix missing images?

It can trigger viewport-based lazy loading on some pages. Confirm that the page behaves that way and wait for the target image after scrolling.

Does Selenium’s standard screenshot save the full page?

The Selenium Chromium API describes saving the current window to PNG. Treat that as a window capture; full-page capture may require a different browser-specific method.

Official references