ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Screenshot of a Website on a Schedule with Selenium

Use Selenium with Firefox to capture a full-page screenshot, then schedule the Python script with your host's scheduler. Includes readiness, storage, and troubleshooting guidance.

By the ScreenshotNeo team4 October 20268 min read

For a documented full-document screenshot in Python, use Selenium with Firefox and call driver.save_full_page_screenshot(). Selenium’s reviewed Firefox API documents this method and byte/base64 alternatives. The reviewed Chromium API documents screenshots of the current window; do not assume its generic screenshot method captures the whole document. Run the script on a recurring schedule using the scheduler available in your environment. Firefox WebDriver API · Chromium WebDriver API.

1. Install Selenium and prepare Firefox

Use a Python environment and a Firefox installation compatible with the Selenium version you install. Selenium’s driver setup can vary by environment, so confirm that Firefox and its WebDriver are available where the scheduled process will run, not only on your development machine.

python -m venv .venv
# macOS or Linux:
. .venv/bin/activate
# Windows PowerShell:
# .venv\Scripts\Activate.ps1
python -m pip install --upgrade selenium

The example below uses Selenium’s Firefox-specific full-page method. It writes a PNG to an absolute path, checks the method’s Boolean result, and closes the browser even if navigation or capture fails.

2. Create a full-page capture script

from datetime import datetime, timezone
from pathlib import Path
import logging
import re

from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait

URL = "https://example.com/"
OUTPUT_DIR = Path("captures").resolve()
WAIT_SECONDS = 30


def safe_name(value: str) -> str:
    """Make a short filename component from a URL host."""
    return re.sub(r"[^A-Za-z0-9.-]+", "_", value)[:120] or "page"


def main() -> None:
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    options = Options()
    options.add_argument("-headless")
    driver = webdriver.Firefox(options=options)
    try:
        driver.set_page_load_timeout(60)
        driver.get(URL)

        # Navigation completion does not guarantee that dynamic content is ready.
        # Replace this with a selector meaningful for the page you capture.
        WebDriverWait(driver, WAIT_SECONDS).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )
        WebDriverWait(driver, WAIT_SECONDS).until(
            lambda d: d.execute_script("return document.body !== null")
        )

        stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
        host = safe_name(driver.current_url.split("/")[2])
        output = OUTPUT_DIR / f"{host}-{stamp}.png"
        saved = driver.save_full_page_screenshot(str(output))
        if not saved:
            raise OSError(f"Selenium could not write screenshot: {output}")
        logging.info("Saved %s", output)
    finally:
        driver.quit()


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
    main()

Replace URL and, for dynamic pages, the generic readiness conditions with a wait for the actual content you need, such as a known article heading or a loading indicator disappearing. Navigation reaching complete is not proof that lazy images, client-rendered sections, or animations have settled.

Write bytes or base64 instead

If another part of the program owns file handling, use the documented full-page byte method:

png_bytes = driver.get_full_page_screenshot_as_png()
output.write_bytes(png_bytes)

The Firefox API also documents a base64-returning method. Decode its result before writing bytes; do not save the base64 text as if it were a PNG. Direct file saving returns a Boolean and documents False on an I/O error, so check it.

Current-window capture is different

The reviewed Chromium API documents save_screenshot(filename) and get_screenshot_as_file(filename) for the current window. The WebDriver windows documentation likewise describes a screenshot of the current browsing context. These references do not establish full-document behavior for those generic calls. If you select Chromium or another browser and need the entire document, verify the exact method with your browser, driver, and Selenium versions before relying on it. Selenium: working with windows and screenshots.

3. Make the page ready for capture

A full-page API controls the captured document extent; it does not decide which content is meaningful. Choose a readiness condition that matches the target site:

  • Wait for a page-specific selector that appears when the main content is rendered.
  • Wait for a loading indicator to disappear if the site exposes one.
  • For lazy-loaded pages, scroll through the document in increments and allow images or sections to load before capture. The exact strategy is site-specific; verify that the content is present before saving.
  • For animated content, wait for the relevant animation to finish or use page-specific setup to reach a stable state.
  • Set a bounded timeout. A missing selector should fail the run clearly instead of leaving a browser open indefinitely.

For example, replace the generic wait with a selector that exists on your target:

from selenium.webdriver.common.by import By

WebDriverWait(driver, 30).until(
    lambda d: d.find_element(By.CSS_SELECTOR, "main article h1").is_displayed()
)

This is an implementation pattern, not a universal guarantee: choose a selector that actually signals readiness on the page you own or monitor.

4. Schedule recurring runs

Selenium captures when the script runs; it does not provide the recurring schedule. Use the scheduler supported by the machine or service that hosts the script. Configure its working directory, Python executable, environment variables, output location, and log destination explicitly. The schedule and scheduler syntax depend on your operating system and deployment environment.

  1. Run the script manually as the same account that will run the scheduled job.
  2. Use an absolute path to the script, Python interpreter, and output directory.
  3. Set required environment variables, including secrets if the target requires authentication, in the scheduler’s environment or secret store.
  4. Set a cadence appropriate to the page and storage budget. Ensure a previous run cannot pile up another browser process if the capture takes longer than expected.
  5. Send standard output and errors to logs, and configure the scheduler to report a nonzero exit status.
  6. Decide how long to retain captures and rotate or delete old files.

For production runs, catch failures at the job boundary if you need structured alerts, but return a failure exit code when navigation or saving fails. Silent success with a missing image makes scheduled capture hard to operate.

5. Output, browser, and capture options

Decision Guidance
Browser Firefox is the browser with documented full-document methods in the reviewed Python API. Verify the selected browser’s supported method if using another browser.
Headless mode The example starts Firefox headlessly for scheduled operation. Confirm that the deployment host has the required browser libraries and permissions.
Output form Save directly to a PNG, retrieve PNG bytes, or retrieve base64 and decode it. Use absolute paths and check file-write success.
Filename Include a UTC timestamp or another unique run identifier to avoid overwriting prior captures. Sanitize URL-derived filename components.
Readiness Use a page-specific selector or condition. Generic navigation completion may precede lazy content or client-side rendering.
Retention Set a retention policy based on how often you capture and how long you need to compare results.

6. Troubleshooting

Symptom Likely cause What to do
AttributeError for save_full_page_screenshot The installed browser binding, Selenium version, or selected browser does not expose the Firefox method. Use Firefox with a compatible Selenium binding, confirm the installed version and API, or verify the selected browser’s supported full-document approach.
The screenshot shows only the visible viewport A current-window screenshot method was used instead of a documented full-document method. For the documented Python full-document API, use Firefox’s save_full_page_screenshot() or byte method. Verify other browser approaches directly.
The page is blank or missing sections The page had not rendered the desired content, a selector wait was too broad, or scripts/resources failed. Wait for a meaningful page-specific condition, inspect the page state and logs, and confirm the target is reachable from the scheduled host.
Lazy images are absent Images may load only after scrolling into view. Scroll through the relevant document and allow loading to finish before capture; verify the result for that site.
Timeout during navigation or wait The site is slow, unreachable, or the chosen condition never becomes true. Use bounded timeouts, log the failing stage, check network access and the selector, and decide whether a failed run should be retried.
Screenshot file is missing The scheduler used a different working directory, lacks write permissions, or the API returned False. Use an absolute writable output path, create its parent directory, run as the scheduled account, and check the return value.
Works manually but fails on schedule The scheduled job may have a different environment, PATH, working directory, user, or browser dependencies. Use explicit executable and file paths, set environment variables, log output, and test under the same account and launch context.
Old screenshots overwrite new ones The filename is constant. Add a timestamp or unique run ID, then configure retention separately.
Browser processes accumulate Cleanup is skipped on an exception or overlapping runs create concurrent sessions. Keep driver.quit() in a finally block and prevent overlapping scheduled executions where needed.

7. Performance, reliability, and cost

Capture time depends on page load, readiness waits, browser startup, page length, and dynamic resources. A full-document image can be larger and slower to store than a viewport capture. Measure the runtime and output size on your own pages, set timeouts, and avoid a cadence that overlaps the prior run.

For reliable operation, use unique filenames, explicit paths, bounded waits, browser cleanup, scheduler logs, and a retention policy. Consider whether a transient navigation or rendering failure warrants a limited retry; retries should be bounded so one unavailable page does not consume the whole schedule.

Selenium itself is open-source software, but this workflow also uses compute, browser dependencies, and storage supplied by your environment. Budget for those resources and retained files. The research sources do not provide a universal runtime, storage estimate, or scheduler cost.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its one-call API can return a screenshot; request full-page capture for a whole document. 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);

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. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does Selenium’s generic screenshot call always capture the full page?

No. The documented WebDriver screenshot operation is for the current browsing context, and the reviewed Chromium API describes current-window capture. Use and verify a browser-specific full-document API for your chosen browser.

Can the screenshot be returned without writing a file?

Yes. Firefox’s documented full-page API can return PNG bytes or base64, which your application can store or transmit.

Does Selenium run the schedule?

No. The script performs a capture when launched. A scheduler in the hosting environment must invoke it at the desired times.

Will waiting for page load include lazy-loaded images?

Not necessarily. Page readiness is site-specific; wait for the content you need and verify that lazy resources have loaded before saving.