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)
| File | Size | Uploaded | |
|---|---|---|---|
| website2pdf-1.0.0.tar.gz | 31.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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