Skip to main content

Stealth browser CLI that bypasses WAFs and bot detection

Project description

Phantom

Stealth browser CLI that bypasses WAFs and bot detection. Built on Playwright with anti-detection patches so you can fetch pages that block headless browsers, curl, and wget.

Works as a CLI tool for humans and outputs structured JSON for AI agents.

Install

pip install phantom-browse

Chromium is installed automatically on first run. To install it manually:

python -m playwright install chromium

Usage

# Get page text
phantom https://example.com

# Structured JSON output (for agents)
phantom https://example.com --json

# Screenshot
phantom https://example.com --screenshot page.png

# Login flow
phantom https://app.com/login \
  --fill "input[name=email]=user@example.com" \
  --fill "input[name=password]=secret" \
  --click "button[type=submit]" \
  --session myapp \
  --json

# Reuse session (skip login next time)
phantom https://app.com/dashboard --session myapp --json

JSON Output

When using --json, Phantom returns:

{
  "ok": true,
  "url": "https://example.com",
  "title": "Example Domain",
  "text": "...",
  "screenshot": null,
  "console_errors": []
}

On failure:

{
  "ok": false,
  "url": "https://example.com",
  "error": "Navigation failed: net::ERR_NAME_NOT_RESOLVED"
}

Interaction Flags

Actions execute in order, before the final page capture.

--click SELECTOR        # Click element (coordinate-based, human-like timing)
--fill SEL=VAL          # Fill input field (instant)
--type SEL=VAL          # Type into input (80ms per-character delay)
--wait SELECTOR         # Wait for element to appear

Chain them:

phantom https://app.com \
  --wait "form" \
  --fill "input[name=q]=test" \
  --click "button[type=submit]" \
  --json

Multi-Step Plans

For complex flows (login, navigate, interact, capture), use --actions with a JSON plan file:

phantom --actions plan.json --json
phantom https://app.com --actions plan.json --session myapp --json

Plan format — array of steps, each with action, params, and optional description/critical:

[
  {"action": "goto", "params": {"url": "https://app.com/login"}, "description": "Open login page"},
  {"action": "fill", "params": {"selector": "input[name=email]", "value": "me@x.com"}},
  {"action": "fill", "params": {"selector": "input[name=password]", "value": "secret"}},
  {"action": "click", "params": {"selector": "button[type=submit]"}, "critical": true},
  {"action": "wait_for", "params": {"selector": ".dashboard"}, "critical": true},
  {"action": "screenshot", "params": {"path": "/tmp/dashboard.png"}},
  {"action": "goto", "params": {"url": "https://app.com/profile"}},
  {"action": "get_text", "params": {"selector": ".profile-info"}}
]

If a step marked critical: true fails, remaining steps are skipped.

Supported actions: goto, click, fill, type_text, wait_for, wait_for_spa_idle, screenshot, get_text, evaluate, select_option, press_key, login, assert_visible, assert_text_contains, assert_url_contains, get_element_count, get_inner_html, get_table_data

Multi-step output:

{
  "ok": true,
  "steps_total": 8,
  "steps_executed": 8,
  "steps_passed": 8,
  "aborted_at": null,
  "url": "https://app.com/profile",
  "steps": [
    {"ok": true, "action": "goto", "description": "Open login page", "url": "https://app.com/login"},
    {"ok": true, "action": "fill", "description": "fill"},
    ...
  ]
}

Options

--json              Structured JSON output
--actions FILE      JSON file with multi-step action plan
--screenshot PATH   Save full-page screenshot
--session NAME      Cookie persistence (~/.phantom/sessions/)
--timeout MS        Navigation timeout (default: 30000)
--headless          Force headless mode
--headed            Force headed mode (requires display/Xvfb)
--locale LOCALE     Browser locale (default: en-US)
--timezone TZ       Browser timezone (default: America/New_York)
--viewport W H      Viewport size (default: 1366 768)
--min-delay SECS    Min seconds between requests to same domain (default: 2.0, 0 to disable)
--verbose           Debug logging

What It Bypasses

Phantom bundles these anti-detection techniques:

  • playwright-stealth — masks automation fingerprints
  • WebDriver spoofing — hides navigator.webdriver
  • Blink feature flags — disables AutomationControlled
  • Realistic fingerprint — Chrome 131 User-Agent, configurable locale/timezone/viewport
  • Headed mode — uses real GUI via Xvfb when available (harder to detect than headless)
  • Human-like input — variable click timing, coordinate-based clicks, realistic type delays

Using With AI Agents

Phantom is designed to be called by AI agents via subprocess. The --json flag gives structured output any agent can parse:

import subprocess, json

result = subprocess.run(
    ["phantom", "https://example.com", "--json"],
    capture_output=True, text=True
)
data = json.loads(result.stdout)
if data["ok"]:
    page_text = data["text"]

Rate Limiting

Phantom enforces a 2-second per-domain cooldown by default to prevent abuse. If you call Phantom twice against the same domain in quick succession, the second call waits automatically.

phantom https://example.com --json         # goes through immediately
phantom https://example.com --json         # waits ~2s before requesting

phantom https://other-site.com --json      # different domain, no wait

Adjust with --min-delay:

phantom https://example.com --min-delay 5   # 5s between requests (be polite)
phantom https://localhost:3000 --min-delay 0 # disable for local/owned apps

Responsible Use

Phantom is built for legitimate purposes: testing your own apps, accessing data you're authorized to retrieve, and building AI agents that browse the web respectfully.

Do:

  • Test your own web applications
  • Access sites and data you have permission to use
  • Build agents that respect robots.txt and rate limits
  • Use the default rate limiting

Don't:

  • Scrape sites that prohibit it in their ToS
  • Bypass authentication you're not authorized to use
  • Use Phantom to overwhelm or degrade services
  • Disable rate limiting against sites you don't own

The built-in rate limiter exists for a reason. If you find yourself setting --min-delay 0 against someone else's site, reconsider.

License

MIT

Project details


Download files

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

Source Distribution

phantom_browse-0.3.1.tar.gz (12.1 kB view details)

Uploaded Source

Built Distribution

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

phantom_browse-0.3.1-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file phantom_browse-0.3.1.tar.gz.

File metadata

  • Download URL: phantom_browse-0.3.1.tar.gz
  • Upload date:
  • Size: 12.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for phantom_browse-0.3.1.tar.gz
Algorithm Hash digest
SHA256 bcd0be33dce103b8dac1616a5647dc80216d5f5e136785c840ccfdc00f3d2133
MD5 80ab866ecca178eb6068a118eb9a0e6a
BLAKE2b-256 51c113d3b9fe2afe884852e226b747ad4097ae1aa322399e4c2fe1e0266dad56

See more details on using hashes here.

File details

Details for the file phantom_browse-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: phantom_browse-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 15.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for phantom_browse-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b910829b918503ab64200c832e136d1fbccb4342f975ebe736b1addd459428cf
MD5 d78e0fc880823a646e0391bb589ccbfa
BLAKE2b-256 d340aaf114c54f187f1186de61f44edfc45c7410239c49632cb712f6c444feb2

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