How to Use PhantomJS with Python
PhantomJS was controlled from Python through Selenium and GhostDriver. Learn the legacy setup, its limits, and how to migrate to headless Chrome.
Direct answer: Python did not call PhantomJS’s page API directly. The historical pattern was to run the separate PhantomJS executable and control it through Selenium’s WebDriver interface, backed by the embedded GhostDriver. PhantomJS scripts and page methods such as page.open were JavaScript. Because PhantomJS development is suspended and its GitHub repository is archived, use a maintained browser for new automation. The current Python example below uses Selenium with headless Chrome.
This guide separates the legacy architecture from a supported migration path so you can identify what old code is doing without mistaking it for a current installation recommendation.
1. Understand the PhantomJS and Python relationship
PhantomJS was a scriptable headless browser based on QtWebKit. Its use cases included automation, screenshots, headless testing, and network monitoring. Its own page API was JavaScript: for example, JavaScript code could call page.open(url, callback), whose callback received a status such as success or fail. [PhantomJS project] [page.open API]
Python automation historically sat outside that JavaScript runtime. Python used Selenium WebDriver to send browser commands to the PhantomJS executable; GhostDriver, included with PhantomJS, implemented the WebDriver connection. The distinction matters when you are reading old examples: page.open belongs in a PhantomJS JavaScript script, while Python Selenium code issued WebDriver operations.
PhantomJS’s documented CLI reference is for version 2.1.1. It describes running a JavaScript file as phantomjs [options] somescript.js and a --webdriver mode whose default address is 127.0.0.1:8910. PhantomJS 2.1 was released January 23, 2016; the official project says development is suspended, and its repository was archived on May 30, 2023. [PhantomJS command-line reference] [release history] [archived GitHub repository]
2. Run a historical PhantomJS page script
If you need to inspect a legacy script or reproduce an old environment, the basic PhantomJS CLI model is JavaScript, not Python.
// save as capture.js
var page = require('webpage').create();
page.open('https://example.com', function (status) {
if (status !== 'success') {
console.error('Could not load the page: ' + status);
phantom.exit(1);
return;
}
page.render('example.png');
phantom.exit(0);
});
phantomjs capture.js
The callback reports whether opening the page succeeded; it does not guarantee that every asynchronous application request or late-loading image has finished. PhantomJS APIs, browser behavior, and bundled WebKit are legacy and may differ from current browsers. The official documentation describes page.open and its callback behavior. [page.open API]
Starting the old WebDriver endpoint
PhantomJS’s CLI documentation also describes starting its embedded GhostDriver in WebDriver mode:
phantomjs --webdriver=127.0.0.1:8910
The endpoint can then be addressed by a compatible WebDriver client. This documents the historical architecture; it is not a guarantee that a current Selenium release will work with PhantomJS. The supplied sources do not establish a contemporary Python Selenium and PhantomJS version pairing, so treat old calls such as webdriver.PhantomJS(...) as version-dependent legacy code, not as a current copy-and-run solution. [PhantomJS command-line reference]
3. Use Selenium with headless Chrome in Python
For a new Python project, choose a maintained browser that reflects the browser engine your application needs to support. Selenium documents browser automation with Chrome, Edge, Firefox, Safari, WebKitGTK, and WPEWebKit. Selenium Manager generally handles driver setup when you create a WebDriver instance. [Selenium WebDriver documentation] [Selenium Python first script]
Install and run
python -m pip install selenium
# save as capture.py
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
print(driver.title)
driver.save_screenshot("example.png")
finally:
driver.quit()
python capture.py
This opens the page in Chrome’s headless mode, prints its title, saves a viewport screenshot, and closes the browser even if an operation raises an exception. Chrome’s Selenium documentation lists --headless=new among commonly used arguments. [Selenium Chrome documentation]
Wait for a page condition instead of sleeping blindly
Modern pages often render content after the initial navigation returns. Wait for the element or state your task actually needs; choose a locator that matches your page.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
heading = WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "h1"))
)
print(heading.text)
driver.save_screenshot("example.png")
finally:
driver.quit()
Explicit waits make the readiness condition visible and bounded. A fixed sleep may waste time on fast runs while still being too short on slow ones. Do not wait for network silence if the site maintains long-lived requests; wait for a task-specific element instead.
4. Choose the right replacement approach
| Need | Good starting point | Why |
|---|---|---|
| Python-driven interactions, forms, navigation, or browser tests | Selenium with the browser engine your users need | It exercises a real maintained browser through WebDriver. |
| Cross-browser tests | Selenium with the relevant supported browsers, locally or through Remote WebDriver/Grid | Pick browsers based on the engines and environments your application must support. |
| Only retrieve HTML or parse data, with no JavaScript interaction | An HTTP client and HTML parser | A full browser adds setup and resource cost if the page does not need browser execution. |
| Get a rendered website screenshot without managing a browser | ScreenshotNeo | One API request returns a screenshot or PDF; clean captures remove known consent banners, popups, and chat widgets before capture. |
No single browser is right for every test suite. Choose based on browser support, maintenance and security updates, setup and driver management, headless behavior, local versus remote execution, and whether the job needs UI interaction or only HTTP and parsing. Selenium supports local browser scripts and optional remote execution through Selenium Server/Grid. [Selenium WebDriver documentation]
5. Configuration and operational choices for Selenium
Headless and headed runs
Use --headless=new for a headless Chrome run. For debugging a visual or timing issue, remove the headless argument and run with a visible browser. Headless execution still has browser and environment dependencies; it is not a promise that rendering will be identical across operating systems, fonts, browser versions, or GPU configurations.
Driver management
Start with Selenium Manager by creating webdriver.Chrome(); Selenium documents it as generally handling driver setup at instantiation. If you install and manage ChromeDriver manually, keep its major version aligned with the Chrome browser major version, as described in Chrome’s official driver guidance. [Selenium WebDriver documentation] [Selenium Chrome documentation]
Local versus remote execution
Local WebDriver launches a browser on the machine running the Python process. Remote WebDriver sends commands to a Selenium server or Grid, which runs the browser elsewhere. Remote execution can centralize browser environments and support multiple configurations, but it adds network, server, and capacity dependencies. Keep credentials and browser session data out of logs and source control.
Screenshot scope
driver.save_screenshot(...) captures the current viewport. For a full document, element-specific captures, custom browser contexts, or reliable handling of site-specific overlays, build and verify the behavior needed for your target browser and page. A viewport screenshot is not automatically a full-page capture.
6. cURL, Python, and Node.js screenshot calls
If the task is simply to obtain a rendered screenshot or PDF and does not require controlling a browser session, a screenshot API can avoid installing a local browser and driver. The examples below use ScreenshotNeo’s documented endpoint and parameter shape; see the ScreenshotNeo API documentation for request options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
Keep the API key secret on a server or in a protected environment variable; do not embed it in public client-side code. ScreenshotNeo supports PNG, JPEG, WebP, and PDF output, plus options for full-page and element capture, viewport/device settings, waits, headers, cookies, and other capture configuration. Its parameter names also accept names used by other screenshot APIs to ease migration. Check the docs for the exact option names and output behavior.
7. Or skip the browser setup
ScreenshotNeo takes a URL in one API call and returns an image or PDF. Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. [API and MCP documentation]
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
webdriver.PhantomJS is missing |
Your Selenium version no longer exposes the old PhantomJS integration, or the example targets a different Selenium generation. | Treat it as legacy code. Migrate to a maintained browser such as Chrome with webdriver.Chrome(); avoid guessing a Selenium/PhantomJS pairing without verifying the exact versions. |
| PhantomJS executable or command is not found | The executable is not installed or is not on the process PATH. | For a legacy reproduction, verify the executable location and CLI version; otherwise migrate instead of depending on an archived project. |
| WebDriver cannot connect to PhantomJS | The executable did not start, the port or address differs, or a local process/network policy blocks it. | Check the PhantomJS process output and configured --webdriver address. The documented default is 127.0.0.1:8910; confirm the client targets the same endpoint. |
| Chrome or driver startup fails | Browser is unavailable, driver resolution failed, or manually managed browser and driver versions are incompatible. | Install Chrome in the runtime, allow Selenium Manager to resolve setup where possible, or align manually installed ChromeDriver’s major version with Chrome. |
| Page is blank or content is missing | The page needs more rendering time, JavaScript execution, authentication, or an element-specific readiness condition. | Wait for the relevant element with an explicit wait; verify URL, authentication, console output, and network access. Avoid assuming navigation completion means application readiness. |
| Screenshot is cropped | The save operation captured only the viewport. | Use a full-page capture approach appropriate to your chosen browser/tool, or capture the needed element. Verify the resulting dimensions and content. |
| Headless output differs from local visual output | Browser version, viewport, fonts, operating system, timing, or GPU settings differ. | Pin and record the browser/runtime environment, set a deliberate viewport, wait for the page state you need, and compare headed and headless runs. |
| Timeout is intermittent | Slow dependencies, variable page load, overloaded browser host, or an overly broad readiness condition. | Set bounded navigation and explicit-wait timeouts, wait for a task-specific condition, capture diagnostics, and retry only transient failures with a limit. |
9. Performance, reliability, and cost
- Browser startup: launching a browser has fixed setup and memory costs. Reuse a browser session for related work when isolation requirements allow, and always close it in a
finallyblock. For parallel sessions, size concurrency to available CPU and memory. - Wait strategy: waiting for a specific element usually avoids both needless delay and premature capture. Bound waits and collect enough logs to diagnose failures.
- Reliability: pin or record browser versions in repeatable environments. Selenium Manager simplifies setup, while manual driver management requires compatible versions. Archived PhantomJS carries maintenance and compatibility risk; do not use it for new security-sensitive or browser-compatibility testing.
- Cost: local Selenium has no per-screenshot service charge, but consumes compute, storage, engineering time, and browser maintenance. Remote Grid or hosted browser capacity adds infrastructure or service cost. ScreenshotNeo’s listed plans are Free: 1,000/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free. Only clean shots are billed under the stated product policy; check current plan details before adopting them.
10. FAQ
Can I use PhantomJS directly from Python?
Not through its page API. Historically, Python controlled the separate PhantomJS process through Selenium/WebDriver and GhostDriver; PhantomJS page scripts themselves were JavaScript.
Is PhantomJS still maintained?
No. The project says development is suspended, and the repository is archived read-only. Its 2.1 release dates to January 2016. See the official project page and repository status.
What should I use instead of PhantomJS?
For browser interactions and tests, use Selenium with a maintained browser that matches your support target. For a screenshot-only workflow, consider a screenshot API such as ScreenshotNeo.
Does headless Chrome mean no browser installation is needed?
No. Headless mode runs Chrome without its visible window; Chrome still has to be available in the execution environment.
Can I keep old PhantomJS tests running?
Possibly in a pinned legacy environment, but compatibility depends on the exact PhantomJS, Selenium, operating system, and runtime versions. The cited documentation does not establish a current supported combination.
Sources
- PhantomJS official homepage: project status, engine, and use cases.
- PhantomJS CLI reference: JavaScript script invocation and WebDriver mode.
- PhantomJS page.open API: JavaScript API and callback status.
- PhantomJS release history and archived repository.
- Selenium WebDriver documentation, Python first script, and Chrome options.


