The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
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.
#1 Best Overall
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.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteRank #2
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.
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:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11import 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.
Best Value
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.
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.
Quick Recap
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.

