Skip to main content

Playwright-like async API for PhantomJS — pip install only, no apt, no npm, no Chrome.

Project description

Phasma

phasma

Headless browser automation that works with pip install alone.

No apt. No npm. No Chrome. No system dependencies.

PyPI Python License: MIT


Why phasma?

Most headless browser tools require installing Chrome, Chromium, or Node.js at the system level — which isn't always possible.

phasma was built for environments where apt is locked but pip is open: corporate servers, HPC clusters, minimal Docker images, restricted CI/CD pipelines. The PhantomJS binary ships inside the wheel. After pip install phasma, everything works immediately, no further setup required.


Installation

pip install phasma

Requirements: Python 3.10+
Platforms: Linux, macOS, Windows (x86 32-bit & 64-bit)


Quick start

import asyncio
import phasma

async def main():
    browser = await phasma.launch()
    try:
        page = await browser.new_page()
        await page.goto("https://example.com")

        title  = await page.evaluate("document.title")
        heading = await page.text_content("h1")
        print(title, heading)

        await page.screenshot(path="shot.png")
        await page.pdf(path="page.pdf")
    finally:
        await browser.close()

asyncio.run(main())

One persistent PhantomJS process handles every operation — no per-call restarts.


Browser API

The API mirrors Playwright so it feels familiar.

launch() / connect()

browser = await phasma.launch()

Browser

Method Returns Description
new_page() Page Open a new page
new_context(options?) BrowserContext Create an isolated context
close() Shut down the browser
is_connected() bool Whether the process is alive

Page

Navigation

await page.goto(url, timeout=30_000, wait_ms=0)
await page.set_viewport_size(1280, 720)

DOM / JavaScript

title   = await page.evaluate("document.title")
text    = await page.text_content("h1")
html    = await page.inner_html("#content")
result  = await page.eval_on_selector("a", "this.href")
element = await page.wait_for_selector(".loaded", timeout=5000)

Interaction

await page.click("#submit")
await page.fill("#email", "user@example.com")

Output

png_bytes = await page.screenshot("out.png")
pdf_bytes = await page.pdf("out.pdf", format="A4", landscape=False, margin="1cm")

ElementHandle

el = await page.wait_for_selector(".result")
await el.click()
await el.fill("value")
text = await el.text_content()
html = await el.inner_html()

Errors

phasma.Error        # base browser error
phasma.TimeoutError # operation exceeded timeout

SVG Rendering

Convert SVG to PNG, JPEG, or PDF — no Inkscape, no cairosvg, no extra installs.

from phasma.svg import SvgRenderer

async with SvgRenderer() as r:
    png = await r.to_png("<svg ...>...</svg>")
    jpg = await r.to_jpeg(Path("diagram.svg"), scale=2.0)
    pdf = await r.to_pdf("chart.svg", output="chart.pdf", pdf_landscape=True)

One SvgRenderer instance = one PhantomJS process reused across all conversions.

Method Options
to_png(source, output?, scale?, background?) PNG bytes
to_jpeg(source, output?, scale?, background?) JPEG bytes
to_pdf(source, output?, scale?, pdf_format?, pdf_landscape?, pdf_margin?) PDF bytes — pdf_format=None fits paper to SVG size

source accepts an SVG string, a file path string, or a Path object.


Utility functions

For simple one-shot operations without managing a browser session:

# render HTML
html = await phasma.render_page_content("<h1>Hello</h1>")
html = await phasma.render_url_content("https://example.com")

# execute JavaScript
result = await phasma.execute_js_script("1 + 1")

# screenshot / PDF
await phasma.take_screenshot("https://example.com", "out.png")
await phasma.generate_pdf("https://example.com", "out.pdf")

# sync versions (no asyncio.run needed)
html = phasma.sync_render_page_content("<h1>Hello</h1>")
phasma.sync_take_screenshot("https://example.com", "out.png")

Examples

Scraping

async with phasma.launch() as browser:  # also works as context manager
    page = await browser.new_page()
    await page.goto("https://example.com")

    data = await page.evaluate("""({
        title: document.title,
        links: Array.from(document.querySelectorAll('a'))
                    .map(a => ({ text: a.textContent.trim(), href: a.href }))
    })""")

Form interaction

page = await browser.new_page()
await page.goto("https://example.com/login")
await page.fill("#username", "user@example.com")
await page.fill("#password", "secret")
await page.click("#submit")
await page.wait_for_selector(".dashboard")

Batch screenshots

browser = await phasma.launch()
try:
    for url in urls:
        page = await browser.new_page()
        await page.goto(url, timeout=15_000)
        name = url.replace("https://", "").replace("/", "_") + ".png"
        await page.screenshot(name)
finally:
    await browser.close()

Batch SVG export

svgs = Path("assets").glob("*.svg")

async with SvgRenderer() as r:
    for svg in svgs:
        await r.to_png(svg, output=svg.with_suffix(".png"), scale=2.0)

CLI

# driver management
phasma driver --version
phasma driver --path
phasma driver download --force

# run a PhantomJS script directly
phasma driver exec script.js
phasma driver exec script.js --ssl --timeout 30

# render HTML to stdout or file
phasma render-page file.html
phasma render-page file.html -o out.html --viewport 1920x1080 --wait 500
phasma render-url https://example.com -o page.html --wait 2000

# execute JavaScript
phasma execjs "document.title"
phasma execjs -                          # read from stdin

# screenshot
phasma screenshot https://example.com shot.png --viewport 1280x720 --wait 1000

# PDF
phasma pdf https://example.com doc.pdf --format A4 --landscape --margin 2cm

# SVG conversion
phasma svg diagram.svg -o diagram.png
phasma svg diagram.svg -o diagram.png --scale 2.0
phasma svg diagram.svg -o diagram.jpg --format jpeg --background white
phasma svg diagram.svg -o diagram.pdf --format pdf
phasma svg diagram.svg -o diagram.pdf --format pdf --pdf-format A4 --landscape
phasma svg - -o out.png                  # read SVG from stdin

Troubleshooting

Installing from source — binary is not bundled when cloning the repo. Run once:

phasma driver download

Timeout errors — increase the timeout parameter in goto() or wait_for_selector().

SSL errors — set OPENSSL_CONF="" in your environment, or pass --ssl-protocol=any via CLI.

Check binary path:

phasma driver --path

Testing

pip install -e ".[dev]"
pytest tests/ -v

Contributing

  1. Fork and create a feature branch
  2. Add tests for new functionality
  3. Run pytest tests/ — all must pass
  4. Open a pull request

License

MIT — see LICENSE.


phasma — headless browser automation, batteries included.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

phasma-0.7.1-py3-none-win_amd64.whl (18.2 MB view details)

Uploaded Python 3Windows x86-64

phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl (26.2 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

phasma-0.7.1-py3-none-manylinux_2_17_i686.whl (27.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ i686

phasma-0.7.1-py3-none-macosx_10_9_universal2.whl (17.2 MB view details)

Uploaded Python 3macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file phasma-0.7.1-py3-none-win_amd64.whl.

File metadata

  • Download URL: phasma-0.7.1-py3-none-win_amd64.whl
  • Upload date:
  • Size: 18.2 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for phasma-0.7.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 18c1f89d9a2c5f57e6d4034c5be3bf95e69ad9d23adce944be889c510ab085c6
MD5 16b53e83b8cf4cdbbe2a6b7ddb083863
BLAKE2b-256 3466a61fc0f70a8ed53df4461b99a42356ead3a509d6434b414dfb4f4dd56a30

See more details on using hashes here.

File details

Details for the file phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 ca0c7481bb8f29dda6e4f4eef155eb4a69238ef2d8bc8b4069e68267dd49c1b3
MD5 177ead43a6f8159061d7918ab9274bf8
BLAKE2b-256 dc7ed21440bbac4fe552c8cd6e5a637717d277483450b0470d7675f4a7afc1ca

See more details on using hashes here.

File details

Details for the file phasma-0.7.1-py3-none-manylinux_2_17_i686.whl.

File metadata

  • Download URL: phasma-0.7.1-py3-none-manylinux_2_17_i686.whl
  • Upload date:
  • Size: 27.3 MB
  • Tags: Python 3, manylinux: glibc 2.17+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for phasma-0.7.1-py3-none-manylinux_2_17_i686.whl
Algorithm Hash digest
SHA256 e7857bc1132f711a8579dbff98d433610deb743f59a15a9ac4374919746a89f5
MD5 dc3398c8741ccde567870a347a22f500
BLAKE2b-256 8a089a8ca7fd2e668229bfd3925ee9471d670475c27d9abb6a9e5defd245569f

See more details on using hashes here.

File details

Details for the file phasma-0.7.1-py3-none-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for phasma-0.7.1-py3-none-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 123a225a4e5bfaf4397d661d2a3ec39facefe9d0e7f4782cc3bcc74c75178783
MD5 7a05e9fd8af6359de7d9d94becd69eca
BLAKE2b-256 51898d9b78817b1446a8758e4c8bf38f1ae8aed0e57f5354ad2d4a3418b805c2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page