Skip to main content
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.

Metadata

Release files for phasma 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for phasma 0.7.1
File
phasma-0.7.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
phasma-0.7.1-py3-none-manylinux_2_17_i686.whl Python 3 none Linux glibc 2.17+ x86-32 Details
phasma-0.7.1-py3-none-macosx_10_9_universal2.whl Python 3 none macOS 10.9+ universal2 (ARM64, x86-64) Details

Total release size: 89.0 MB

Release files / phasma-0.7.1-py3-none-win_amd64.whl

Download URL phasma-0.7.1-py3-none-win_amd64.whl
Size 18.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
18c1f89d9a2c5f57e6d4034c5be3bf95e69ad9d23adce944be889c510ab085c6
BLAKE2b-256 checksum
How to use checksums
3466a61fc0f70a8ed53df4461b99a42356ead3a509d6434b414dfb4f4dd56a30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl

Download URL phasma-0.7.1-py3-none-manylinux_2_17_x86_64.whl
Size 26.2 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
ca0c7481bb8f29dda6e4f4eef155eb4a69238ef2d8bc8b4069e68267dd49c1b3
BLAKE2b-256 checksum
How to use checksums
dc7ed21440bbac4fe552c8cd6e5a637717d277483450b0470d7675f4a7afc1ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / phasma-0.7.1-py3-none-manylinux_2_17_i686.whl

Download URL phasma-0.7.1-py3-none-manylinux_2_17_i686.whl
Size 27.3 MB
Tags Linux glibc 2.17+ x86-32 Python 3
SHA-256 checksum
How to use checksums
e7857bc1132f711a8579dbff98d433610deb743f59a15a9ac4374919746a89f5
BLAKE2b-256 checksum
How to use checksums
8a089a8ca7fd2e668229bfd3925ee9471d670475c27d9abb6a9e5defd245569f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / phasma-0.7.1-py3-none-macosx_10_9_universal2.whl

Download URL phasma-0.7.1-py3-none-macosx_10_9_universal2.whl
Size 17.2 MB
Tags Python 3 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
123a225a4e5bfaf4397d661d2a3ec39facefe9d0e7f4782cc3bcc74c75178783
BLAKE2b-256 checksum
How to use checksums
51898d9b78817b1446a8758e4c8bf38f1ae8aed0e57f5354ad2d4a3418b805c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.7.1 This release

4 release files

0.7.0

4 release files

0.5.0

4 release files

0.4.1

4 release files

0.4.0

4 release files

0.3.0

4 release files

0.2.0

4 release files

0.1.0

4 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page