Skip to main content

WebSite2PDF

PyPI Python versions CI MIT licence Ruff Checked with mypy

Documentation • PyPI • Changelog • News • Chat

WebSite2PDF

Render web pages and local HTML files to PDF, from Python or from the shell. Fully typed, async-capable, and driven by headless Chromium through Playwright.

from website2pdf import Client

with Client() as client:
    client.convert("https://example.com", "example.pdf")

Installation

pip install website2pdf

Then download the browser Playwright manages. This step is required, the library has nothing to render with until it has run:

playwright install chromium

For the command line, install the extra as well:

pip install "website2pdf[cli]"

Python 3.10 or newer. The browser download is roughly 150 MB and is shared by every project on the machine.

Usage

One page

from website2pdf import Client

with Client() as client:
    # Get the bytes back...
    data = client.convert("https://example.com")

    # ...or write a file, and get its path.
    path = client.convert("https://example.com", "report.pdf")

    # Name the file after the page title.
    path = client.convert("https://example.com", "{title}.pdf")

Local files work the same way, as a path or a file:// URL:

from pathlib import Path

client.convert(Path("invoice.html"), "invoice.pdf")

Many pages

with Client() as client:
    paths = client.convert_many(
        ["https://example.com", "https://example.org"],
        "out/",
    )

Pages that share a title do not overwrite each other: the second file becomes Title (2).pdf.

Concurrently

AsyncClient renders several pages at once in independent browser contexts, sharing a single browser process.

import asyncio

from website2pdf import AsyncClient


async def main() -> None:
    async with AsyncClient(concurrency=8) as client:
        await client.convert_many(urls, "out/")


asyncio.run(main())

Options

Three immutable option objects, each covering one concern. Set them on the client as defaults, override them per call.

from website2pdf import BrowserOptions, Client, Margin, PdfOptions, RenderOptions

with Client(
    pdf_options=PdfOptions(paper_format="A4", margin=Margin.uniform("1cm")),
    render_options=RenderOptions(wait_until="networkidle"),
    browser_options=BrowserOptions(user_agent="my-crawler/1.0"),
) as client:
    client.convert("https://example.com", "default.pdf")
    client.convert(
        "https://example.com",
        "landscape.pdf",
        pdf_options=PdfOptions(landscape=True, print_background=False),
    )

A page that fills itself in after loading needs an explicit wait:

client.convert(
    "https://example.com",
    "chart.pdf",
    render_options=RenderOptions(wait_for_selector="#chart-ready"),
)

Chromium prints with print media by default. To reproduce what a visitor sees on screen:

render_options = RenderOptions(emulate_media="screen")

The full reference lives in the options documentation.

Command line

website2pdf https://example.com -o report.pdf
website2pdf https://example.com https://example.org -o out/
website2pdf https://example.com --format A5 --landscape --margin 1cm
website2pdf https://example.com -o - > report.pdf          # straight to stdout
cat urls.txt | website2pdf - -o out/ --concurrency 8        # one URL per line

website2pdf --help lists every option.

Errors

Everything the library raises derives from WebSite2PDFError, so one except covers it:

from website2pdf import BrowserNotInstalledError, NavigationError, WebSite2PDFError

try:
    client.convert(url, "out.pdf")
except BrowserNotInstalledError:
    ...  # tells you to run `playwright install chromium`
except NavigationError:
    ...  # the page did not load in time
except WebSite2PDFError:
    ...  # anything else from this library

Notes

Client runs Playwright's synchronous driver on the calling thread, which means asyncio.run() cannot start on that thread while a client is open. In an asyncio program, use AsyncClient. Several Client instances on one thread are fine, they share one reference-counted driver.

PDF rendering is Chromium-only: it is the only engine that exposes a print-to-PDF command.

Migrating from 0.x

Version 1.0 is a rewrite with a new import name and a new API. Nothing from 0.x carries over unchanged.

0.x 1.0
import WebSite2PDF import website2pdf
c.pdf(url) c.convert(url)
c.pdf(url, filename="x.pdf") c.convert(url, "x.pdf")
c.pdf([urls], filename=[names]) c.convert_many(urls, "out/")
pdfOptions={"landscape": True} pdf_options=PdfOptions(landscape=True)
seleniumOptions=["--no-sandbox"] browser_options=BrowserOptions(args=("--no-sandbox",))
delay=3 render_options=RenderOptions(wait_until="networkidle")
"paperWidth" / "marginTop" … width= / Margin(top=...), CSS units
Firefox fallback removed; Chromium only

Client is now a context manager, and convert() returns bytes when you omit the destination and a Path when you give one, never a union of four types. The migration guide walks through it.

Contributing

See CONTRIBUTING.md. In short:

uv sync --all-extras --all-groups
uv run playwright install chromium
uv run pytest

Licence

MIT.

Metadata

Release files for website2pdf 1.0.0

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

Source distribution (sdist)

Source distribution for website2pdf 1.0.0
File Size Uploaded
website2pdf-1.0.0.tar.gz 31.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for website2pdf 1.0.0
File Interpreter ABI Platform
website2pdf-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 57.3 kB

Release files / website2pdf-1.0.0.tar.gz

Download URL website2pdf-1.0.0.tar.gz
Size 31.6 kB
Tags Source
SHA-256 checksum
How to use checksums
910e902b3aaed04c581cdd6c6e29617fc0d39c8e2abc946a30306dec0ff35952
BLAKE2b-256 checksum
How to use checksums
adc5b939e98c6e500a2317bbfec491d4ecb22c51e4ddadc2d489d5f729af96eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 9, 2026.

Transparency log

Release files / website2pdf-1.0.0-py3-none-any.whl

Download URL website2pdf-1.0.0-py3-none-any.whl
Size 25.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e7f00c6e774150867910e3857e92d9e5e41398524ed5db54ad25990bf007098f
BLAKE2b-256 checksum
How to use checksums
261469b8341314b710f70efcce86cf29b88417ee7997a37d62e5032508ff8678
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.1.1

2 release files

0.1.0

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