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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For a maintained Python desktop application, use Qt WebEngine with PySide6: load the URL in a QWebEngineView, wait for loadFinished, then call printToPdf. For a quick shell command, wkhtmltopdf is simpler. PhantomJS and Ghost.py are legacy paths best reserved for existing code you need to keep running.

Choose the method that fits your project

These options are not interchangeable wrappers around one renderer. They differ in maintenance, how you automate them, JavaScript behavior, and how much control you have over page layout. The available documentation does not provide a controlled comparison of rendering fidelity or speed, so there is no evidence-based universal “best” renderer.

Method Best fit What to know
Qt WebEngine with PySide6 New or maintained Python application needing browser-based rendering Qt’s documented flow waits for page loading and then generates a PDF asynchronously.
wkhtmltopdf Shell scripts, scheduled tasks, or simple batch conversion One command converts a URL to a PDF using Qt WebKit; it runs headlessly without a display service.
PhantomJS Keeping an existing PhantomJS script operational Open a URL, then render to a filename ending in .pdf; its documentation is legacy.
Ghost.py Maintaining an existing Ghost.py application A Python WebKit client with a PDF method; its documented setup depends on PySide or PyQt and Qt4 printer documentation.

For new code, prefer a currently maintained integration rather than selecting PhantomJS or Ghost.py solely because older examples are easy to find. Confirm compatibility and security requirements against the version and operating environment you will deploy; the legacy documentation cited for PhantomJS and Ghost.py does not establish their current browser compatibility or security posture.

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

Convert a URL to PDF with Python and PySide6 Qt WebEngine

Qt’s official HTML-to-PDF example follows four steps: create a QWebEngineView, start loading the URL, wait for the load to finish, and start PDF generation. The PDF operation is asynchronous. Its completion signal, pdfPrintingFinished, reports whether the file was written successfully. The example below uses that sequence and prints a result before exiting.

Install a PySide6 distribution that includes Qt WebEngine for your platform, save the script as url_to_pdf.py, and run it with a URL and output path:

python url_to_pdf.py https://example.com page.pdf
import sys
from pathlib import Path
from PySide6.QtCore import QUrl
from PySide6.QtWidgets import QApplication
from PySide6.QtWebEngineWidgets import QWebEngineView


def main():
    if len(sys.argv) != 3:
        print("Usage: python url_to_pdf.py URL OUTPUT.pdf", file=sys.stderr)
        return 2

    url, output = sys.argv[1], sys.argv[2]
    if not output.lower().endswith(".pdf"):
        print("Output filename must end in .pdf", file=sys.stderr)
        return 2

    app = QApplication(sys.argv)
    view = QWebEngineView()
    exit_code = 1

    def printed(file_path, success):
        nonlocal exit_code
        if success:
            print(f"PDF saved to {file_path}")
            exit_code = 0
        else:
            print(f"PDF generation failed: {file_path}", file=sys.stderr)
        app.quit()

    def loaded(success):
        if not success:
            print(f"Page load failed: {url}", file=sys.stderr)
            app.quit()
            return
        view.page().printToPdf(output)

    view.page().pdfPrintingFinished.connect(printed)
    view.loadFinished.connect(loaded)
    view.load(QUrl(url))
    app.exec()
    return exit_code


if __name__ == "__main__":
    raise SystemExit(main())

The snippet deliberately waits for the PDF completion signal before the Qt event loop exits. Qt documents that file-path printing is asynchronous and overwrites an existing file at that path; choose a fresh output path if you need to preserve an earlier PDF. Qt also documents an overload that returns PDF bytes through a callback, useful when your application needs to handle the result in memory rather than write directly to a file.

Control the resulting PDF

The code above uses the WebEngine PDF operation’s default print layout. The documented example establishes the loading and printing sequence, but the material here does not establish detailed page-size, margin, or page-range option syntax for PySide6. If those settings matter, use the version-matched Qt WebEngine API documentation before adding them; do not assume another tool’s layout flags apply to Qt.

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

Convert with wkhtmltopdf from the command line

For a shell-based job, install the wkhtmltopdf executable for your operating system and run the project’s documented example, substituting your URL and destination:

wkhtmltopdf https://example.com page.pdf

The project describes wkhtmltopdf as an open-source command-line utility that renders HTML to PDF with Qt WebKit and can run headlessly without a display service. That makes it convenient for scripts and scheduled jobs that need a command rather than a Python UI. It is still a Qt WebKit-based renderer, not a promise of parity with every current browser engine or modern site.

For a basic batch loop, let the shell invoke one conversion per URL and check the command’s exit status before treating the output as valid. The project’s documented example establishes the basic URL-to-file invocation; additional flags for page sizing, headers, cookies, or other rendering behavior should be taken from the installed version’s own help and documentation rather than copied from a different renderer.

Convert with PhantomJS

PhantomJS’s documented approach is to open a URL with page.open(url, callback), verify that the open succeeded, and call page.render('output.pdf'). The output extension selects PDF rendering. Its paperSize setting controls layout: documented paper choices include A3, A4, A5, Legal, Letter, and Tabloid, with portrait or landscape orientation, margins, and optional headers or footers.

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.
var page = require('webpage').create();

page.paperSize = {
  format: 'A4',
  orientation: 'portrait',
  margin: '1cm'
};

page.open('https://example.com', function (status) {
  if (status !== 'success') {
    console.error('Could not load the URL');
    phantom.exit(1);
    return;
  }

  page.render('page.pdf');
  phantom.exit();
});

This illustrates the documented open-then-render sequence and a paper-size configuration. Keep it for legacy scripts whose PhantomJS runtime is already part of a controlled environment; the documentation available for this method is legacy and does not establish present-day compatibility or security posture. For a new Python application, use the Qt WebEngine path above instead of introducing an old runtime without checking its maintenance and deployment implications.

Convert with Ghost.py

Ghost.py is documented as a Python WebKit client that requires PySide or PyQt. Its print_to_pdf method accepts a destination path, paper size, paper margins, and zoom factor; the documentation delegates the details of those layout values to Qt4’s QPrinter documentation.

# Existing Ghost.py projects can use the documented method shape:
# ghost.print_to_pdf(path, paper_size, paper_margins, zoom_factor)

# Example call with values already configured for your Ghost.py/Qt setup:
ghost.print_to_pdf('page.pdf', paper_size, paper_margins, zoom_factor)

This is a method signature, not a standalone script: a working Ghost.py program must first create and configure a Ghost client using the matching installed Ghost.py and Qt bindings. The available documentation does not establish a current installation recipe or a complete runnable setup for present-day Python versions, so do not treat an old PySide/PyQt installation command as universally valid. If you own an existing Ghost.py codebase, retaining it may be reasonable when migration cost is higher than its operational risk; for new work, use a maintained Qt WebEngine integration.

Or skip the browser setup

If you want a hosted request instead of installing and operating a local rendering stack, ScreenshotNeo accepts a URL and can return a screenshot or PDF. For a normal one-call image capture, use the supplied Python example:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

See the ScreenshotNeo API documentation for PDF output options and the other request parameters; the code above is the documented image-output example and does not guess a PDF parameter. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture, and bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan.

Sign up for ScreenshotNeo to try 1,000 free screenshots a month with no card.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting URL-to-PDF conversion

  • The PDF is missing or empty with Qt. Do not exit as soon as the page-load signal fires. Wait for pdfPrintingFinished; file-path printing completes asynchronously. Check the signal’s success value and the destination path.
  • Qt reports that loading failed. The documented flow begins PDF generation only after loadFinished. Check the URL and whether the target can load in the environment where the script runs; do not proceed to print after a failed load.
  • A previous PDF disappeared. Qt’s file-path print operation overwrites an existing file. Select a new output path or move the old file before generating another.
  • wkhtmltopdf cannot find a display. The project describes its command-line tool as able to run headlessly without a display service. Check that the executable is installed and available on the job’s PATH, and use the exact command format shown above.
  • PhantomJS does not produce a PDF. Confirm that the open callback reports success, that rendering happens inside that callback, and that the output filename ends in .pdf. The documented render format follows the extension.
  • Ghost.py setup fails on a newer environment. Its documented dependency is PySide or PyQt and its PDF method references Qt4 printer documentation. Verify the exact binding and runtime compatibility of the existing deployment before attempting upgrades or migration.

Reliability, layout, and cost decisions

All four local methods avoid paying a per-request hosted API fee, but they shift responsibility to your application or operating environment: install and maintain the renderer, handle failures, and decide how to validate resulting files. This is a trade-off, not a measured cost comparison; the available tool documentation does not provide common pricing, speed, or fidelity benchmarks.

For recurring jobs, treat successful navigation and successful PDF writing as separate checkpoints. Qt explicitly exposes both the load result and the later PDF-finished signal. Preserve those distinctions in logs so a failed page request is not mistaken for a print failure, or vice versa. For PhantomJS, check the open status before rendering. For command-line conversions, check the process result and verify the output file before downstream use.

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

Layout controls vary. PhantomJS documents paper formats, orientation, margins, and headers or footers; Ghost.py exposes paper size, margins, and zoom through its method; Qt’s cited conversion flow documents asynchronous printing, while detailed layout options must be checked in the API for the Qt version you use. wkhtmltopdf is the most direct command-line choice here, but consult the installed command’s documentation for any advanced options you require.

Which option should you use?

  • Choose Qt WebEngine with PySide6 for a maintained Python application where you can manage a Qt WebEngine dependency and need an asynchronous browser-based PDF flow.
  • Choose wkhtmltopdf when the job is a simple shell command or batch task and Qt WebKit rendering meets the need.
  • Keep PhantomJS only when maintaining an existing script and its legacy runtime is acceptable in your environment.
  • Keep Ghost.py when preserving an existing application is more practical than migrating it, after verifying its Qt and Python compatibility.

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.