Skip to main content

infra-screenshot

PyPI Python Versions Build Status License: AGPL v3 License: Commercial

infra-screenshot provides reusable models, services, and CLI helpers for capturing website screenshots. It exposes the core abstractions.

For a task-oriented index, see CAPABILITIES.md or inspect screenshot.capabilities from an installed environment. The installed wheel exposes screenshot.capabilities; the source tree and source distribution also include AGENTS.md, examples/, agent-snippets/, and skills/.

Table of Contents

Features

  • 🎭 Multiple backends: Support for both Playwright and Selenium
  • 📸 Flexible capture: Single screenshots or batch processing
  • 🔧 Configurable viewports: Desktop, mobile, and custom viewport sizes
  • 💾 Storage abstractions: Local filesystem or cloud storage backends
  • 🚀 Async/await support: Modern async architecture for better performance
  • 🛠️ CLI tools: Ready-to-use command-line interface
  • 🔄 Retry logic: Built-in retry with exponential backoff for reliability
  • 🎨 Visual cleanup: Conservatively hide common consent/dialog overlays and disable animations for cleaner screenshots

Installation

# Install with Playwright backend (includes bundled Chromium)
uv pip install "infra-screenshot[playwright]"
uv run playwright install chromium

# Add GIF motion snapshot support when needed
uv pip install "infra-screenshot[playwright,motion]"

# OR install with Selenium backend (requires system Chrome)
uv pip install "infra-screenshot[selenium]"

Using pip

# Install with Playwright backend
pip install "infra-screenshot[playwright]"
playwright install chromium

# Add GIF motion snapshot support when needed
pip install "infra-screenshot[playwright,motion]"

# OR install with Selenium backend
pip install "infra-screenshot[selenium]"

Quick Verification

# Check installation
screenshot local -h

# Test capture
screenshot local --urls https://example.com --output-dir ./test-screenshots

Usage

CLI: Local Screenshot Capture

The CLI provides a local subcommand for capturing screenshots locally.

Basic Examples

# Capture a single URL
screenshot local --urls https://example.com --output-dir ./screenshots

# Capture multiple URLs (repeat the --urls flag for each URL)
screenshot local \
  --urls https://example.com \
  --urls https://github.com \
  --output-dir ./screenshots

# For many URLs, use a JSONL input file (recommended)
screenshot local --input urls.jsonl --output-dir ./screenshots

# Capture with custom settings
screenshot local \
  --urls http://localhost:3000 \
  --output-dir ./screenshots \
  --viewports desktop mobile \
  --depth 0 \
  --scroll false \
  --allow-autoplay true

Input File Format (JSONL)

For batch processing, create a file with one JSON object per line:

{"url": "https://example.com", "job_id": "example"}
{"url": "https://github.com", "job_id": "github"}
{"url": "https://docs.python.org", "job_id": "python-docs"}

Then run:

screenshot local --input urls.jsonl --output-dir ./screenshots

Each non-empty JSONL line must be an object with a non-empty url. Invalid records fail fast with a file-and-line validation error. Each record can also include optional job_id, backend, metadata, nested options, and nested artifacts fields. For a richer template using screenshot_options/v2 plus optional HTML/GIF artifacts, see examples/cli_jsonl_batch_capture.py. Motion artifacts are configured through JSONL artifacts payloads or the programmatic API rather than direct --motion-* CLI flags.

Common Options

Option Description Default
--viewports Viewport presets (desktop, mobile, tablet) desktop
--depth Link depth to follow (0 = single page only) 0
--scroll Enable scrolling before capture false
--scroll-step-delay-ms Delay between scroll steps (ms) 250
--max-scroll-steps Max scroll iterations 15
--full-page Capture entire page height (not just viewport) false
--timeout-s Page load timeout in seconds 60
--post-nav-wait-s Wait after navigation (settling time) 2.5
--pre-capture-wait-s Wait before screenshot 1.5
--hide-overlays Conservatively hide common consent/dialog overlays true
--disable-animations Disable CSS animations for cleaner shots true
--allow-autoplay Allow media autoplay true
--mute-media Mute audio/video true
--block-media Block video/audio requests false
--site-concurrency Number of sites to capture in parallel 1
--max-viewport-concurrency Number of viewports captured in parallel per page 1
--max-pages Max pages per site (when following links) 1

See all options:

screenshot local -h

The CLI exits non-zero when any job is partial or failed. Partial jobs keep any artifacts already written and are reported in the JSON summary with outcome: "partial". Viewport preset typos now fail fast instead of falling back silently, and Playwright honors --post-nav-wait-s exactly without adding hidden settle waits. Overlay hiding is intentionally conservative; disable --hide-overlays if the target site uses overlay-like class names for real content. If you are upgrading existing consumers, see docs/contract-hardening-migration.md.

Real-World Examples

Capture homepage only (no scrolling, viewport-only):

screenshot local \
  --urls http://localhost:3000 \
  --output-dir ./tmp \
  --depth 0 \
  --scroll false \
  --full-page false

Full-page screenshot with scrolling:

screenshot local \
  --urls http://localhost:3000 \
  --output-dir ./tmp \
  --depth 0 \
  --scroll true \
  --full-page true

Capture multiple viewports:

screenshot local \
  --urls https://example.com \
  --output-dir ./screenshots \
  --viewports desktop mobile tablet

Python API: Programmatic Usage

For integration into your own tooling, call the async runner directly with a configured ScreenshotOptions payload:

from pathlib import Path
import asyncio

from screenshot import ScreenshotCaptureOutcome, ScreenshotOptions, capture_screenshots_async
from screenshot.models import CaptureOptions

async def capture_example() -> None:
    options = ScreenshotOptions(
        capture=CaptureOptions(
            enabled=True,
            viewports=("desktop",),
            depth=0,
            scroll=False,
        )
    )

    result = await capture_screenshots_async(
        "demo-job",
        "https://example.com",
        store_dir=Path("screenshots"),
        partition_date=None,
        options=options,
    )

    if result.outcome is ScreenshotCaptureOutcome.SUCCESS:
        print(f"Captured {result.captured} screenshot(s)")
    elif result.outcome is ScreenshotCaptureOutcome.PARTIAL:
        print(f"Partially captured {result.captured} screenshot(s)")
        for error in result.errors:
            print(f"Partial failure: {error.message}")
    else:
        for error in result.errors:
            print(f"Capture failed: {error.message}")

asyncio.run(capture_example())

For auxiliary HTML/GIF artifacts, pass artifacts=ScreenshotArtifactsRequest(...) to capture_screenshots_async(). See examples/custom_viewport_and_html_snapshot.py and examples/playwright_motion_snapshot.py.

For complete runnable scripts covering CLI batches, Selenium, ScreenshotService, and custom viewport flows, see the repository examples below.

Repository Examples

The repository ships copyable examples for both CLI and programmatic usage:

Browser Setup

Playwright: Bundled Chromium vs System Chrome

By default, Playwright uses its own bundled Chromium (installed via playwright install chromium). This provides:

  • ✅ Reproducibility: Known browser version across environments
  • ✅ No system dependencies: Works in containers/CI without system Chrome
  • ✅ Headless-first design: Optimized for automation

When to use system Chrome (--playwright-executable-path):

  • 🎯 Testing against real Chrome (not Chromium)
  • 🎯 Using Chrome extensions or enterprise policies
  • 🎯 Matching end-user browser versions exactly
  • 🎯 Debugging with Chrome DevTools locally

Trade-offs:

Aspect Bundled Chromium System Chrome
Setup playwright install chromium Install Chrome + ensure compatibility
Version control Pinned to Playwright release Depends on system updates
Size ~300MB download Already on system
Reproducibility ✅ High (version-locked) ⚠️ Lower (varies by system)
Extensions ❌ Not supported ✅ Supported
DevTools Limited Full local debugging

Usage example with system Chrome:

screenshot local \
  --urls https://example.com \
  --output-dir ./screenshots \
  --playwright-executable-path /usr/bin/google-chrome-stable

Need a deeper comparison? Check the repository's .dev_docs/playwright_vs_selenium_linux.md for codec/DRM support, driver management, and when to switch to system Chrome.

Finding Chrome path:

# Linux/WSL
which google-chrome-stable    # Usually /usr/bin/google-chrome-stable
which chromium-browser         # Usually /usr/bin/chromium-browser

# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome

# Windows (WSL path)
/mnt/c/Program\ Files/Google/Chrome/Application/chrome.exe

If the path is invalid, the tool logs a warning and falls back to bundled Chromium automatically.

Installing System Chrome/Chromium

For Playwright (Optional - only if using system Chrome)

# Google Chrome (stable) - Linux/WSL
wget -O /tmp/chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install -y /tmp/chrome.deb

# OR Chromium (from distro packages)
sudo apt-get update && sudo apt-get install -y chromium-browser fonts-liberation

For Selenium (Required)

Selenium requires a system Chrome/Chromium install. On modern Selenium 4 setups, Selenium Manager will usually locate or download a matching driver automatically, so webdriver-manager is not required for the normal case.

# Install Chrome/Chromium (as above)

If your environment cannot use Selenium Manager, install a compatible chromedriver manually and make it available on PATH. Advanced programmatic callers can also provide an explicit driver path via runner options.

Configuration

Environment Variables

Runtime behavior can be customized via environment variables:

Variable Description Default
SCREENSHOT_SCROLL_STEP_DELAY_MS Delay between scroll steps (ms) 250
SCREENSHOT_MAX_SCROLL_STEPS Maximum scroll iterations 15
PLAYWRIGHT_CAPTURE_MAX_ATTEMPTS Retry attempts for failed captures 3
SCREENSHOT_RETRY_BACKOFF_S Initial retry delay (seconds) 0.5
SCREENSHOT_RETRY_MAX_BACKOFF_S Maximum retry delay (seconds) 5.0
SCREENSHOT_ENABLE_TIMING Enable additional timing/performance metrics during capture (see docs/perf-testing.md) See docs/perf-testing.md

Example:

export SCREENSHOT_SCROLL_STEP_DELAY_MS=200
export PLAYWRIGHT_CAPTURE_MAX_ATTEMPTS=5
screenshot local --urls https://example.com --output-dir ./screenshots

Logging

infra-screenshot uses Python's standard logging module. Enable diagnostics in your application or CLI runs with:

import logging

logging.basicConfig(level=logging.INFO)
logging.getLogger("screenshot.playwright_runner").setLevel(logging.DEBUG)

Logger namespaces:

Logger Purpose
screenshot.playwright_runner Playwright capture + upload lifecycle
screenshot.selenium_runner Selenium fallback pipeline
screenshot.cli CLI orchestration and batch processing

Log records include structured extra={...} fields such as job_id, url, and viewport. URLs are sanitized before logging to prevent leaking SAS tokens or credentials; configure your formatter (JSON/text) to emit those keys for easier filtering.

OpenTelemetry correlation

When using OpenTelemetry, attach trace/span IDs to screenshot logs so traces and logs stay aligned:

import logging
from pathlib import Path

from opentelemetry import trace

from screenshot import capture_screenshots_async

tracer = trace.get_tracer(__name__)
url = "https://example.com/products"
job_id = "otel-demo"

with tracer.start_as_current_span("screenshot-job") as span:
    logger = logging.getLogger("screenshot.playwright_runner")
    logger.info(
        "Starting screenshot job",
        extra={
            "job_id": job_id,
            "trace_id": span.get_span_context().trace_id,
            "span_id": span.get_span_context().span_id,
        },
    )
    options = ...  # Build ScreenshotOptions as shown above
    await capture_screenshots_async(
        job_id,
        url,
        store_dir=Path("/tmp/screens"),
        partition_date=None,
        options=options,
    )

Contributing

We welcome contributions! To get started with development:

  1. Read the contributing guide: CONTRIBUTING.md
  2. Set up your development environment (covered in CONTRIBUTING.md)
  3. Run tests and linters before submitting PRs

For bug reports and feature requests, please open an issue.

License

This project is dual-licensed:

AGPL-3.0 (Open Source)

Free for open-source and non-commercial use under the GNU Affero General Public License v3.0.

Key requirement: If you run this software as a service (SaaS, API, web app), you must make your complete source code available under AGPL-3.0.

Commercial License

For commercial use without AGPL obligations (proprietary products, SaaS without open-sourcing, etc.).

See LICENSE for full details.


Need help? Check out:

Metadata

Release files for infra-screenshot 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for infra-screenshot 0.3.1
File Size Uploaded Provenance
infra_screenshot-0.3.1.tar.gz 119.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for infra-screenshot 0.3.1
File Interpreter ABI Platform
infra_screenshot-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 194.9 kB

Release files / infra_screenshot-0.3.1.tar.gz

Download URL infra_screenshot-0.3.1.tar.gz
Size 119.7 kB
Tags Source
SHA-256 checksum
How to use checksums
8aa777dd77d46b18d59b8c7dc9642b1fadc597ea9db30bb29123029b3a43dee3
BLAKE2b-256 checksum
How to use checksums
bd9069fce4e20669184339d31cd92825af49f6d238aef5a0949b8a88b8f4b696
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 16, 2026.

Transparency log

Release files / infra_screenshot-0.3.1-py3-none-any.whl

Download URL infra_screenshot-0.3.1-py3-none-any.whl
Size 75.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c4089aa31169c3b260ffdb9da54ef1e57e486110137ed54a9ea7c759e128a9ef
BLAKE2b-256 checksum
How to use checksums
a78735af74ab4688f1f75ba1bdbc98da77717ba96971e33f7b308fca7ae8c675
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 16, 2026.

Transparency log

Security for infra-screenshot 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for infra_screenshot-0.3.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page