Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a PDF made from HTML you control, use WeasyPrint: it provides a Python-centered HTML/CSS-to-PDF workflow. For an existing page that relies on JavaScript or browser behavior, use Playwright to render it in a browser and create a PDF. Neither choice guarantees an exact match for every page; check the result against your actual content, styles, fonts, and resources.

Choose a renderer for the page you have

The main decision is not simply which library has the shortest code. It is whether your input is a controlled document or a page whose output depends on a browser. WeasyPrint is designed to render HTML and CSS to PDF. Playwright controls a browser page and offers browser-based PDF output, including print-media behavior.

Situation Start with What to consider
A report, invoice, or document generated from a template you control WeasyPrint Give it the HTML and its assets; use a meaningful base URL if the HTML is a string with relative paths.
An existing page whose content or layout depends on browser JavaScript Playwright Navigate to the page, wait for essential content, then create the PDF. The page’s print styles are used by default.
A page that requires authentication, cookies, or browser-specific behavior Evaluate Playwright WeasyPrint’s default HTTP client does not support advanced features such as cookies or authentication; its guide describes a custom URL fetcher for such cases.
A page that must look identical to a particular browser view Test Playwright and the target page together Do not assume either renderer will match without checking the generated PDF on representative pages.

This is a practical distinction based on the documented interfaces, not a claim that one renderer always looks better or runs faster. Rendering fidelity and operating cost depend on the particular page and deployment.

Convert controlled HTML to PDF with WeasyPrint

WeasyPrint’s HTML object accepts a URL, filename, file object, or HTML source string. Call write_pdf() to save the result to a target, or call it without a target to get PDF bytes. The project describes itself as a visual rendering engine for HTML and CSS that can export to PDF; it is not a full WebKit or Gecko browser engine. That makes it a natural starting point for controlled templates, but not a promise of complete browser equivalence.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install and save a file from a URL

A basic Python environment can install the package with pip install weasyprint. Platform installation requirements can vary, so check the WeasyPrint installation documentation for your operating system before deploying. The WeasyPrint 70.0 documentation describes support for Python 3.10 or later on CPython and PyPy; confirm the requirements for the version you install.

from weasyprint import HTML

source_url = "https://example.com/report"
output_path = "report.pdf"

HTML(url=source_url).write_pdf(output_path)
print(f"Wrote {output_path}")

Replace the example URL and output filename with your own. This is most suitable when the server can retrieve the page and its resources and the page does not depend on browser-side execution to construct its final content. If it does, use a browser workflow or test whether the content is present in the HTML WeasyPrint receives.

Render generated HTML and preserve relative assets

If your application builds an HTML string in memory, pass it as string=. Relative image and stylesheet paths need a base URL: without one, a path such as images/logo.png has no reliable resource root to resolve against. Set base_url to the intended directory or URL root. Alternatively, make asset URLs absolute where that is appropriate.

from weasyprint import HTML

html = """
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="assets/report.css">
  </head>
  <body>
    <h1>Quarterly report</h1>
    <img src="assets/logo.png" alt="Company logo">
  </body>
</html>
"""

pdf_bytes = HTML(
    string=html,
    base_url="/srv/myapp/templates/",
).write_pdf()

with open("report.pdf", "wb") as output:
    output.write(pdf_bytes)

Here, base_url is an example filesystem resource root; use the real directory or URL root that makes sense in your application. Because the call has no target, write_pdf() returns bytes, which can be written to a file or passed to another part of your application. The result is still only as complete as the resources the renderer can resolve.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Convert a browser-rendered webpage with Playwright

Use Playwright when you need a browser page as the rendering context. A typical flow is to launch a browser, open a page, navigate to the target, wait for the content your PDF needs, and call page.pdf(). Playwright’s Python Page API says PDF output uses print CSS by default. If the site’s screen styles are specifically required, call page.emulate_media(media="screen") before generating the PDF.

Install Playwright with pip install playwright and install the browser binary used by your environment, for example with playwright install chromium. Browser installation and runtime requirements depend on the environment; verify them for your deployment.

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as playwright:
        browser = await playwright.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://example.com", wait_until="load")

        # PDF output uses print media by default.
        await page.pdf(
            path="page.pdf",
            format="A4",
            print_background=True,
            margin={
                "top": "12mm",
                "right": "12mm",
                "bottom": "12mm",
                "left": "12mm",
            },
        )
        await browser.close()

asyncio.run(main())

This example waits for the page load event, but that does not prove that every application-specific, delayed, or lazy-loaded item is ready. If the content you need appears later, wait for an appropriate page condition before calling page.pdf(). Avoid treating a fixed delay as proof that a page is complete: the right condition depends on the site.

Choose print or screen styling deliberately

For a document intended to print, keep Playwright’s default print-media behavior and define print-specific layout with CSS such as @media print and @page. If the PDF should use screen styling instead, emulate screen media before exporting:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.emulate_media(media="screen")
await page.pdf(path="page.pdf", format="A4")

Playwright documents named paper formats, including A4 and Letter, as well as page dimensions and margins. Set the paper format or dimensions and margins to match the intended document rather than relying on an unstated default. Confirm the behavior of your chosen options with your installed Playwright version and inspect the resulting PDF.

Control pagination, page size, and assets

HTML that looks acceptable in a browser window can paginate poorly. Plan the document for pages, not just for a scrolling viewport. Print styles and @page rules can help control page size, margins, and breaks, but check which controls your selected engine honors and review the output visually.

  • Set the paper size and margins. Use the renderer’s PDF options and/or print CSS, with one deliberate source of truth where possible.
  • Keep important content together. Use print-specific page-break rules for headings, tables, and other blocks where a split would make the document hard to read; verify the actual result.
  • Check backgrounds and images. Browser PDF options and CSS can affect whether colored backgrounds appear. Confirm that logos, fonts, and other resources loaded rather than assuming a successful PDF call means every asset rendered.
  • Resolve relative paths. For WeasyPrint HTML strings, provide a meaningful base_url. For either workflow, check that the render process can access the assets it references.
  • Use zoom cautiously in WeasyPrint. Its documentation warns that non-default zoom scales all CSS units, including physical units and named page sizes such as A4. It is not a harmless fit-to-page adjustment when physical dimensions matter.

Do not make a fidelity decision from a single easy page. Preview PDFs generated from representative pages, including pages with long content, tables, images, and the styles or browser-dependent content that matter to your application.

Common problems and practical fixes

Symptom Likely cause What to check
Images or styles are missing in a WeasyPrint PDF made from a string Relative paths have no useful base URL, or the resource cannot be accessed. Pass the correct base_url, use deliberate absolute or local asset URLs, and confirm the renderer can retrieve each resource.
A page is blank or lacks content that appears in a browser The final page content may depend on browser-side behavior, or the browser workflow may export before essential content is ready. Check whether the content exists in the HTML being rendered. For a dynamic page, use Playwright and wait for the relevant content or state before calling page.pdf().
The PDF uses different styling from the browser window Playwright uses print CSS by default. Decide whether print or screen media is appropriate. Use print styles for a print document, or emulate screen media before PDF output when screen styles are required.
Page dimensions seem wrong after changing WeasyPrint zoom Zoom also changes the scale of physical CSS units and named page sizes. Restore the intended zoom and set page size and margins explicitly instead of using zoom as an unexamined fit-to-page fix.
An authenticated page does not render with WeasyPrint’s default client The default HTTP client does not support advanced features such as cookies or authentication. Consider Playwright for a browser-based workflow, or investigate the custom URL-fetcher option described in WeasyPrint’s guide. Follow the site’s access rules.
The PDF is valid but looks wrong on a particular page PDF generation succeeded, but the target’s resources, fonts, print CSS, page breaks, or timing do not produce the expected layout. Inspect the actual PDF, check resource loading and pagination, then adjust the template or wait condition and test again on that page.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Handle untrusted HTML and resource access carefully

WeasyPrint’s security guide warns: “Using WeasyPrint with untrusted HTML or untrusted CSS may lead to various security problems.” Treat user-supplied HTML and CSS as potentially unsafe input, not as a harmless formatting choice. Rendering can involve fetching referenced URLs or files, so consider what resources the rendering process is allowed to access, particularly when it runs in a service that handles content from outside your application.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This article does not prescribe a hardening configuration: the appropriate controls depend on your deployment and the renderer version. Consult the security guidance for the version you operate. Do not assume that changing from WeasyPrint to a browser automatically resolves resource-access risks. For authenticated or private pages, use only resources and access methods you are authorized to use; test the behavior in the environment where the PDF will be produced.

Or skip the browser setup

If your task is to capture a webpage rather than build a custom Python rendering pipeline, ScreenshotNeo offers a screenshot API and MCP server for developers. Its API accepts a URL in a GET request and can return a screenshot or PDF. For a one-call screenshot example, replace the placeholder key with your API key. See the ScreenshotNeo API documentation for options.

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

ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses include X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. See ScreenshotNeo for the service details, or sign up free.

FAQ

Does this process alter the original webpage?

No. The examples produce a separate PDF file; they do not edit the source page.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I use these methods for pages I do not control?

Only where you are authorized to access and render the page. Respect the site’s access rules and take care with private content and credentials.

Frequently Asked Questions

Does this process alter the original webpage?

No. The examples produce a separate PDF file; they do not edit the source page.

Can I use these methods for pages I do not control?

Only where you are authorized to access and render the page. Respect the site’s access rules and take care with private content and credentials.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.