Skip to main content

Async Python browser automation framework powered by Chrome DevTools Protocol.

Project description

Noctra Browser

CI Python License: MIT

Async Python browser automation framework powered by Chrome DevTools Protocol.

Project Status

Noctra Browser is an async-first framework for launching Chrome or Chromium, connecting over CDP, controlling pages, evaluating JavaScript, using CSS selectors, handling events, taking screenshots, managing cookies, and driving human-like input.

It ships with a built-in stealth layer engineered to defeat detection that also catches the evasions themselves:

  • No Runtime.enable. JavaScript is evaluated inside an isolated world, so the single most reliable CDP automation signal never fires.
  • No Proxy-based hooks. Property spoofs are installed with native-looking getters, defeating the prototype-chain ownKeys inspector leak and the Error.stack getter trap that expose naive stealth tooling.
  • toString and descriptor integrity. Every patched function reports [native code], and Object.getOwnPropertyDescriptor stays faithful (e.g. navigator.webdriver remains a native getter on Navigator.prototype).
  • Worker consistency. Evasions are re-applied inside web/shared/service workers via Target.setAutoAttach, so navigator.platform/hardwareConcurrency/user-agent match the main thread — a gap nodriver leaves open.
  • Fingerprint surface. Spoofs navigator.webdriver/plugins/mimeTypes/languages/vendor/platform/hardwareConcurrency/deviceMemory, builds a realistic window.chrome, fixes the permissions API, spoofs the WebGL vendor/renderer, adds deterministic (stable) canvas/audio noise, and strips the HeadlessChrome user-agent marker.

Installation

pip install -e ".[dev]"

Noctra Browser requires Python 3.11+ and a local Chrome or Chromium-compatible browser.

Basic Usage

import asyncio
from noctra_browser import launch


async def main() -> None:
    async with await launch(headless=False) as browser:
        page = await browser.new_page()
        await page.goto("https://example.com")
        await page.wait_for_load_state("domcontentloaded")

        title = await page.title()
        heading = await page.locator("h1").text()

        await page.screenshot(path="example.png")

        print(title, heading)


asyncio.run(main())

Launch Options

from noctra_browser import Viewport, launch

browser = await launch(
    headless=True,
    executable_path=None,
    user_data_dir=None,
    args=["--lang=en-US"],
    env=None,
    timeout=30.0,
    viewport=Viewport(width=1280, height=720),
)

If executable_path is not supplied, Noctra Browser searches common Chrome and Chromium locations on macOS, Windows, and Linux. If user_data_dir is omitted, a temporary profile directory is created and cleaned up when the browser closes.

Stealth

Stealth is enabled by default. To configure or disable it, pass the stealth argument:

from noctra_browser import StealthConfig, launch

# Disable entirely (uses Runtime.enable, faster, detectable)
browser = await launch(stealth=False)

# Fine-tune the spoofed fingerprint
browser = await launch(
    stealth=StealthConfig(
        languages=("en-US", "en"),
        platform="Win32",
        vendor="Google Inc.",
        hardware_concurrency=8,
        device_memory=8,
        webgl_vendor_string="Intel Inc.",
        webgl_renderer_string="Intel Iris OpenGL Engine",
        canvas_noise=True,
        audio_noise=True,
        spoof_workers=True,
    ),
)

When stealth is active, page.evaluate() runs in an isolated world, so page scripts cannot observe the evaluation context. Custom evasions can be injected via StealthConfig(extra_evasions=(...,)); they run in both the main world and workers.

Human-like Input

element = await page.query("#search")
await element.type("query", human=True)   # variable per-key delay
await element.fill("fast value")          # Input.insertText, no per-key events

await page.locator("#submit").click()     # stepped mouse movement + press/release

Async Context Manager

async with await launch(headless=True) as browser:
    page = await browser.new_page()
    await page.goto("https://example.com")

You can also manage cleanup manually:

browser = await launch(headless=True)
page = await browser.new_page()
await page.goto("https://example.com")
await browser.close()

Page Navigation

page = await browser.new_page()
await page.goto("https://example.com", wait_until="load")
await page.reload(wait_until="domcontentloaded")
print(await page.url())
print(await page.title())

Locators

heading = page.locator("h1")
print(await heading.text())

button = page.locator("button[type='submit']")
await button.click()

Locators are lazy. The selector is resolved when an action or read method runs.

Screenshots

await page.goto("https://example.com")
await page.screenshot(path="example.png", full_page=True)

image_bytes = await page.screenshot()

Cookies

from noctra_browser import Cookie

await page.set_cookies([
    Cookie(name="theme", value="dark", domain="example.com"),
])

cookies = await page.cookies()

Network Events

from noctra_browser import Request, Response


def on_request(payload: object) -> None:
    if isinstance(payload, Request):
        print(payload.method, payload.url)


def on_response(payload: object) -> None:
    if isinstance(payload, Response):
        print(payload.status, payload.url)


page.on("request", on_request)
page.on("response", on_response)
await page.goto("https://example.com")

Console Events

from noctra_browser import ConsoleMessage


def on_console(payload: object) -> None:
    if isinstance(payload, ConsoleMessage):
        print(payload.type, payload.text)


page.on("console", on_console)
await page.evaluate("console.log('hello')")

JavaScript Evaluation

title = await page.evaluate("document.title")
answer = await page.evaluate("(a, b) => a + b", 20, 22)

Error Handling

from noctra_browser import NoctraError, TimeoutError

try:
    await page.wait_for_selector("main h1", timeout=2.0)
except TimeoutError as exc:
    print(exc)
except NoctraError as exc:
    print(f"Browser automation failed: {exc}")

All framework-specific exceptions inherit from NoctraError.

Development Setup

pip install -e ".[dev]"
python -m pytest
python -m mypy noctra_browser
python -m ruff check noctra_browser tests examples
python -m ruff format noctra_browser tests examples

Testing

The unit tests cover CDP command id generation, response matching, CDP error conversion, async-aware events, selector validation, timeout behavior, public imports, and a Chrome launch smoke test. The smoke test skips automatically when the environment does not have a usable Chrome or Chromium executable.

Ethical Usage

Noctra Browser is designed for authorized automation, QA, testing, research, and internal tooling. Do not use it for abuse, spam, credential attacks, unauthorized scraping, or bypassing platform protections.

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

noctra_browser-0.1.0.tar.gz (43.4 kB view details)

Uploaded Source

Built Distribution

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

noctra_browser-0.1.0-py3-none-any.whl (48.6 kB view details)

Uploaded Python 3

File details

Details for the file noctra_browser-0.1.0.tar.gz.

File metadata

  • Download URL: noctra_browser-0.1.0.tar.gz
  • Upload date:
  • Size: 43.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for noctra_browser-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e8be1159f297b379f80eff0ff51d87826fae2a8515f578fe1da01727823f5fe5
MD5 b190d0febaff64ae0e707ac4aca8dcd4
BLAKE2b-256 303cf986985e23e1dfcf026bef029b4555ef29132b80b747f9f956f94a8dd29c

See more details on using hashes here.

File details

Details for the file noctra_browser-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: noctra_browser-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 48.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for noctra_browser-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7128fd127e241c8a9f550f615e928df0474e587d1615fc18381476f8d1dac897
MD5 4c8eeee60871ab0abc6b757a475f59ad
BLAKE2b-256 0d51201cb3c93e8b852d511ae6cf9ca776e312bb3645e82f55fd5f03ffd68163

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