Skip to content
Cascading Labs QScrape VoidCrawl Yosoi

API Reference

API Reference

Generated from voidcrawl v0.4.1. Only symbols in __all__ are listed.

Configuration

BrowserConfig

Configuration for launching or connecting to a single browser instance.

Controls headless/headful mode, stealth patches, proxy routing, and custom Chrome flags. Pass an instance to BrowserSession or embed one inside PoolConfig. Attributes:

  • headless bool — Run Chrome without a visible window. Defaults to True.
  • stealth bool — Apply anti-detection patches (navigator overrides, etc.). Defaults to True.
  • no_sandbox bool — Disable the Chrome sandbox. Required in some Docker environments. Defaults to False.
  • proxy str | None — Upstream HTTPS proxy URL, e.g. "http://proxy:8080".
  • chrome_executable str | None — Path to a custom Chrome/Chromium binary. When None, the bundled Chromium discovery is used.
  • extra_args list[str] — Additional command-line flags forwarded to Chrome.
  • user_data_dir str | None — Persistent Chrome user data directory. Use this for same-profile anti-bot validation and long-lived local sessions.
  • ws_url str | None — Connect to an already-running Chrome instance via its WebSocket debugger URL instead of launching a new one.
  • port int | None — Pin Chrome’s --remote-debugging-port for a launched browser, so another process can attach to it via ws_url and adopt one of its tabs with BrowserSession.attach_page. None (default) lets the OS pick a free ephemeral port.
  • debug bool — Wrap pages in an interactive step-debugger. When True, BrowserSession.new_page returns a voidcrawl.debug.DebugPage and voidcrawl.actions.Flow.run automatically pauses before each action. Requires the debug extra (uv add 'voidcrawl[debug]'). Defaults to False.
  • stepping bool — Pause before every action when debug=True. Set to False to run freely without stopping. Defaults to True.
  • highlight bool — Flash a red CSS outline on targeted elements when debug=True. Defaults to True.
  • step_delay float — Seconds to wait between actions in non-stepping mode when debug=True. Defaults to 0.3.

model_dump

model_dump() -> dict[str, object]

PoolConfig

Configuration for a pool of reusable browser tabs.

Controls how many Chrome processes to launch, how many concurrent tabs each process may hold, and when tabs are recycled or evicted. Attributes:

  • browsers int — Number of Chrome processes in the pool. Defaults to 1.
  • tabs_per_browser int — Maximum concurrent tabs per Chrome process. Defaults to 4.
  • tab_max_uses int — Hard-recycle a tab after this many navigations. Prevents memory leaks in long-running crawls. Defaults to 50.
  • tab_max_idle_secs int — Evict a tab that has been idle longer than this many seconds. Defaults to 60.
  • acquire_timeout_secs int — Maximum seconds to wait in BrowserPool.acquire when all tabs are checked out. 0 means wait indefinitely. Defaults to 30.
  • chrome_ws_urls list[str] — Pre-existing Chrome WebSocket debugger URLs. When non-empty, the pool connects to these instead of launching new processes, and browsers is ignored.
  • browser BrowserConfig — Shared BrowserConfig applied to every Chrome process launched by the pool.

from_docker

from_docker(headful: bool = False, host: str = 'localhost', ports: list[int] | None = None, tabs_per_browser: int = 4, check: bool = True) -> PoolConfig

Build a PoolConfig for a VoidCrawl Docker container.

Selects the correct default ports for headless or headful mode and optionally probes the Chrome endpoints before returning so you get a clear error message if the container is not running. Args:

  • headful bool — Connect to the headful Docker container (ports 19222/19223). Defaults to False (headless, ports 9222/9223).
  • host str — Hostname where the Docker container is reachable. Defaults to "localhost".
  • ports list[int] | None — Override the default port list. When None, the defaults resolve in this order: explicit CDP_PORTS env var (comma-separated), else CDP_PORT_BASE + [0, 1], else [9222, 9223] for headless / [19222, 19223] for headful.
  • tabs_per_browser int — Max concurrent tabs per Chrome process. Defaults to 4.
  • check bool — Probe each Chrome endpoint before returning and raise RuntimeError with a setup hint if unreachable. Defaults to True.

Returns: PoolConfig — class:PoolConfig with chrome_ws_urls pre-populated.

Raises:

  • RuntimeError — When check=True and a Chrome endpoint is unreachable. The error message includes the docker command needed to start the container.

from_env

from_env() -> PoolConfig

Build a PoolConfig from environment variables.

Reads the following variables (all optional):

+------------------------+---------------------------------+---------+ | Variable | Description | Default | +========================+=================================+=========+ | CHROME_WS_URLS | Comma-separated ws/http URLs | — | +------------------------+---------------------------------+---------+ | BROWSER_COUNT | Chrome processes to launch | 1 | +------------------------+---------------------------------+---------+ | TABS_PER_BROWSER | Max concurrent tabs per browser | 4 | +------------------------+---------------------------------+---------+ | TAB_MAX_USES | Hard recycle threshold | 50 | +------------------------+---------------------------------+---------+ | TAB_MAX_IDLE_SECS | Idle eviction timeout | 60 | +------------------------+---------------------------------+---------+ | ACQUIRE_TIMEOUT_SECS| Max seconds for acquire() | 30 | +------------------------+---------------------------------+---------+ | CHROME_NO_SANDBOX | Set to "1" to disable | — | +------------------------+---------------------------------+---------+ | CHROME_HEADLESS | Set to "0" for headful | 1 | +------------------------+---------------------------------+---------+ | AUTO_EVICT | Set to "0" to disable | 1 | +------------------------+---------------------------------+---------+ Returns: PoolConfig — A fully-populated PoolConfig.

from_profile

from_profile(profile: ScaleProfile = 'balanced', env: str = 'auto') -> PoolConfig

Build a PoolConfig from a resource-aware scale profile.

Measures current system resources and returns a pool configuration tuned to the requested aggressiveness level. Args:

  • profile ScaleProfile — One of "minimal", "balanced" (default), or "advanced".
  • env str — Environment hint passed to voidcrawl.scale.compute_scale. "auto" (default) detects automatically from system facts.

Returns: PoolConfig — class:PoolConfig sized for the detected resources.

Raises:

  • “ — exc:~voidcrawl.scale.InsufficientResourcesError: When the machine lacks the minimum resources to launch Chrome.

model_dump

model_dump() -> dict[str, object]

Sessions & Pools

BrowserPool

Pool of reusable browser tabs across one or more Chrome processes.

Manages a semaphore-bounded set of recycled tabs. Tabs are navigated to about:blank on release rather than closed, making subsequent acquires near-instant. Tabs are hard-recycled after PoolConfig.tab_max_uses navigations and evicted after PoolConfig.tab_max_idle_secs of inactivity. Args:

  • config PoolConfig — Pool sizing and browser launch options.

acquire

acquire() -> _AcquireContext

Check out a tab from the pool as an async context manager.

The tab is automatically returned to the pool when the context exits, even on exception. Returns: _AcquireContext — An async context manager yielding a PooledTab.

warmup

warmup() -> None

Pre-open tabs across all browser sessions.

Call after entering the pool context to eliminate cold-start latency on the first acquire calls.

BrowserSession

Async context manager wrapping a single Chromium instance via CDP.

Use as an async with block. On entry the browser is launched (or connected to, if BrowserConfig.ws_url is set); on exit the process is terminated and resources are freed. Args:

  • config BrowserConfig | None — Browser launch options. Defaults to BrowserConfig() (headless + stealth).

attach_page

attach_page(target_id: str) -> Page

Adopt an existing tab by its CDP target_id.

Unlike new_page, this opens no new tab and does not re-apply stealth — it wraps the live tab the browser already has. Use it from a second process attached via BrowserConfig.ws_url to drive the exact tab another driver is on (e.g. to solve a captcha in place, so the minted token lands in that tab). Obtain target_id from Page.target_id (or from the VoidCrawl MCP session_open result). Args:

  • target_id str — The CDP target id of the tab to adopt.

Returns: Page — class:Page handle bound to the existing tab.

close

close() -> None

Shut down the browser process immediately.

Called automatically on __aexit__; only needed if you want to close the browser without leaving the async with block.

new_page

new_page(url: str | None = None) -> Page

Open a blank tab, or navigate a new tab to url when provided.

When BrowserConfig.debug is True, returns a voidcrawl.debug.DebugPage wrapper that automatically triggers interactive step-debugging when passed to voidcrawl.actions.Flow.run. Args:

  • url str | None — The URL to load in the new tab.

Returns: Page — The new tab handle (or a debug wrapper when debug=True).

page

page(url: str | None = None) -> _PageContext

Create a page context that always closes its tab.

The tab is closed on success, exception, timeout, and cancellation. If close also fails while another exception is active, the original exception is preserved.

version

version() -> str

Return the browser version string (e.g. "Chrome/126.0.6478.126"). Returns: str — The Chrome/Chromium product version reported by the browser.

websocket_url

websocket_url() -> str

The browser’s CDP WebSocket endpoint (ws://…).

Hand this to another process (with a tab’s target_id) so it can attach to the same Chrome via BrowserConfig(ws_url=…) and adopt that tab with attach_page. Returns: str — The ws:// debugger URL of the underlying browser.

Page

A single browser tab created via BrowserSession.new_page.

add_init_script

add_init_script(script: str) -> None

Install JavaScript before each subsequent document executes.

arm_download

arm_download(dir: str, max_bytes: int | None = None) -> DownloadCapture

Arm an action-triggered download capture into dir.

Perform the triggering action next (e.g. click_by_role), then pass the returned capture to wait_download. Use for downloads started by a page action — a “Download” button, a generated/cross-origin URL (Google Drive) — rather than download, which needs a URL. voidcrawl.capture_download brackets these as a context manager.

ax_box_in_frame

ax_box_in_frame(frame_url_pattern: str, role: str, name: str, nth: int = 0) -> list[float]

Locate an AX role + name inside a frame; return its on-page rectangle [x, y, width, height] in CSS pixels.

Same cross-frame, closed-shadow-piercing resolution as click_ax_in_frame, but returns the geometry instead of clicking — so you can drive a humanized click yourself (curved approach via dispatch_mouse_event, press at a jittered point in the box). Empty name matches any node of that role.

ax_outline_in_frame

ax_outline_in_frame(frame_url_pattern: str, depth: int | None = None) -> str

Compact accessibility outline of a specific (possibly cross-origin) frame — pierces closed shadow roots. Discover the role / accessible name to pass to click_ax_in_frame.

ax_tree_outline

ax_tree_outline(depth: int | None = None) -> str

Return the AX tree as a compact, indented role "name" outline.

Readable counterpart to get_full_ax_tree: text-noise and hidden nodes are pruned. Same output the MCP session_ax_tree tool renders.

click_ax_in_frame

click_ax_in_frame(frame_url_pattern: str, role: str, name: str, nth: int = 0, humanize: bool = False) -> None

Click an element by AX role + name inside a specific frame.

The cross-frame, shadow-piercing analogue of click_by_role: roots the AX tree at the frame matched by frame_url_pattern and descends into closed shadow roots, then clicks the match at its box-model centre with a real compositor mouse event (a trusted click, unlike a DOM .click()). Reaches widgets the page’s own JS cannot — e.g. Cloudflare Turnstile’s “Verify you are human” checkbox in a closed shadow root inside a cross-origin challenges.cloudflare.com iframe. Empty name matches any node of that role.

Cross-origin google.com / cloudflare frames must be in-process: launch the session with extra_args=["disable-site-isolation-trials"].

click_by_role

click_by_role(role: str, name: str, nth: int = 0, humanize: bool = False) -> None

Click the nth element matching accessibility role + name.

Markup-independent analogue of click_element: resolves via the AX tree, bridges to the DOM, scrolls into view, and clicks. Raises if no such node exists. Args:

  • role str — Computed accessibility role, e.g. "button", "link".
  • name str — Computed accessible name (exact match).
  • nth int — 0-based index when several nodes match.
  • humanize bool — Click at the element’s box-model centre with a humanized compositor pointer path (curved, min-jerk, tremor) instead of a DOM .click(). Off by default.

click_element

click_element(selector: str) -> None

Click the first element matching selector.

click_xy

click_xy(x: float, y: float, humanize: bool = False) -> None

Click at (x, y) with a trusted compositor event (press → release).

With humanize=True the cursor first travels a human-like path there (see move_mouse). The programmatic analogue of the click_visual_coords MCP tool.

close

close() -> None

Close this tab and release its resources.

content

content() -> str

Return the full page HTML.

delete_cookie(name: str, domain: str | None = None, path: str | None = None) -> None

Delete a cookie by name, optionally scoped to a domain and path.

detect_captcha

detect_captcha() -> str | None

Probe DOM for captcha / bot-wall markers.

Returns one of "recaptcha", "hcaptcha", "turnstile", "cloudflare_challenge", "datadome" — or None.

dispatch_key_event

dispatch_key_event(event_type: str, key: str | None = None, code: str | None = None, text: str | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchKeyEvent.

dispatch_mouse_event

dispatch_mouse_event(event_type: str, x: float, y: float, button: str = 'left', click_count: int = 1, delta_x: float | None = None, delta_y: float | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchMouseEvent.

download

download(url: str, dir: str, timeout: float = 120.0, max_bytes: int | None = None) -> DownloadOutcome

Download url into directory dir through this page’s browser context (cookies / fingerprint preserved).

The stream aborts past max_bytes. Treat dir as quarantine and pass the result to scan_file before trusting the file. The CDP download behavior is reset before this returns. Args:

  • url str — Absolute URL of the file to download.
  • dir str — Directory the file is saved into.
  • timeout float — Download timeout in seconds.
  • max_bytes int | None — Abort past this many bytes (default 100 MiB).

eval_js

eval_js(expression: str) -> object

Alias for evaluate_js — short form used by MCP tooling.

eval_js_in_frame

eval_js_in_frame(frame_url_pattern: str, expression: str) -> object

Alias for evaluate_js_in_frame.

evaluate_js

evaluate_js(expression: str) -> object

Evaluate a JavaScript expression and return the result.

evaluate_js_in_frame

evaluate_js_in_frame(frame_url_pattern: str, expression: str) -> object

Evaluate expression inside a (possibly cross-origin) iframe.

The frame is selected by a substring of its URL. The expression runs in that frame’s own execution context (document is the frame’s document) — the way to reach an iframe whose contentDocument is null from the parent under the same-origin policy. Args:

  • frame_url_pattern str — Substring of the target frame’s URL, e.g. "recaptcha/api2/bframe".
  • expression str — JavaScript expression or IIFE string.

Raises:

  • RuntimeError — if no frame matches, or the matched frame has no scriptable execution context.

expect_response

expect_response(pattern: str, timeout: float = 30.0, max_response_bytes: int = 2097152, max_total_bytes: int = 8388608) -> ResponseExpectation

expect_responses

expect_responses(patterns: dict[str, str], timeout: float = 30.0, max_response_bytes: int = 2097152, max_total_bytes: int = 8388608) -> ResponseExpectation

frame_urls

frame_urls() -> list[str]

List the URLs of every frame on the page, in no particular order.

Handy for discovering the right frame_url_pattern to pass to evaluate_js_in_frame.

get_cookies

get_cookies() -> list[dict[str, Any]]

Return all cookies matching the current page URL.

get_full_ax_tree

get_full_ax_tree(depth: int | None = None) -> list[dict[str, Any]]

Return the browser-computed accessibility (AX) tree.

Wraps CDP Accessibility.getFullAXTree. The result is a flat list of AX node dicts linked by childIds/parentId; each node carries role, computed name, properties (state), and backendDOMNodeId. Call after the page has rendered. Args:

  • depth int | None — Maximum descendant depth to traverse. None returns the whole tree.

goto

goto(url: str, timeout: float = 30.0, capture_endpoints: bool = False, wait_until: str = 'networkidle') -> PageResponse

Navigate to url and wait for network idle in one shot. Args:

  • url str — The URL to load.
  • timeout float — Maximum seconds to wait for network idle.
  • capture_endpoints bool — When True, record the data-plane network endpoints (XHR + Fetch request URLs) seen during the load and surface them as PageResponse.endpoints.

move_mouse

move_mouse(x: float, y: float, humanize: bool = False) -> None

Move the virtual cursor to (x, y) via CDP Input.dispatchMouseEvent.

With humanize=True the cursor travels a realistic curved, minimum-jerk, lightly-tremored path (multiple MouseMoved events) from its last position; otherwise it jumps in one event. No page-world JS is injected.

navigate(url: str) -> None

Navigate to url without waiting for any load event.

pdf_bytes

pdf_bytes() -> bytes

Render the page as a PDF and return the raw bytes.

query_ax_tree

query_ax_tree(role: str | None = None, name: str | None = None) -> list[dict[str, Any]]

Query the AX tree (Accessibility.queryAXTree) for matching nodes.

The semantic analogue of query_selector_all: addresses by computed role / accessible name rather than markup. Name matching is exact. Passing neither returns every node under the document root.

query_selector

query_selector(selector: str) -> str | None

Return inner HTML of the first matching element.

query_selector_all

query_selector_all(selector: str) -> list[str]

Return inner HTML of every matching element.

reset_download

reset_download() -> None

Reset this page’s CDP download behavior to Chrome’s default. Call to release an armed-but-unused capture (e.g. on an error path).

screenshot

screenshot(path: str | None = None, bbox: tuple[int, int, int, int] | None = None) -> bytes | str

Capture a PNG screenshot with optional disk output and/or crop. Args:

  • path str | None — If set, writes PNG to this path and returns the path. If omitted, returns raw bytes.
  • bbox tuple[int, int, int, int] | None — Optional (x, y, width, height) in CSS pixels.

screenshot_png

screenshot_png() -> bytes

Capture a full-page screenshot as PNG bytes.

set_cookie(name: str, value: str, domain: str | None = None, path: str | None = None, secure: bool | None = None, http_only: bool | None = None) -> None

Set a cookie on the current page.

set_geolocation

set_geolocation(latitude: float, longitude: float, accuracy: float | None = None) -> None

Override geolocation and grant the geolocation permission.

navigator.geolocation reads require a secure context (https / localhost), not data: URLs. accuracy defaults to 50 metres.

set_headers

set_headers(headers: dict[str, str]) -> None

Set extra HTTP headers for subsequent requests.

set_locale

set_locale(locale: str) -> None

Override the locale (Intl + Accept-Language), e.g. "fr-FR".

set_timezone

set_timezone(timezone_id: str) -> None

Override the timezone by IANA id, e.g. "America/New_York".

target_id

target_id() -> str

Return the CDP target id of this page (stable across same-tab navigations). Pass to BrowserSession.attach_page to re-adopt this exact tab from another connection.

title

title() -> str | None

Return the document title, or None.

type_into

type_into(selector: str, text: str) -> None

Focus and type text into the first matching element.

url

url() -> str | None

Return the current page URL, or None.

wait_download

wait_download(capture: DownloadCapture, timeout: float = 120.0) -> DownloadOutcome

Wait for the armed capture to land a new download. Resets the page’s download behavior. The capture is consumed (single wait).

wait_for_navigation

wait_for_navigation() -> None

Block until the current navigation completes.

wait_for_network_idle

wait_for_network_idle(timeout: float = 30.0) -> str | None

Wait for network activity to settle.

wait_for_selector

wait_for_selector(selector: str, timeout: float = 30.0) -> None

Wait until a CSS selector matches. Event-driven — no polling.

PooledTab

A tab checked out from a voidcrawl.BrowserPool.

Exposes the same page-interaction methods as Page but must not be closed manually — return it to the pool via the async context manager or voidcrawl.BrowserPool.release. Attributes:

  • use_count int — How many times this tab has been acquired (0 on first use).

arm_download

arm_download(dir: str, max_bytes: int | None = None) -> DownloadCapture

Arm an action-triggered download capture; see Page.arm_download.

ax_box_in_frame

ax_box_in_frame(frame_url_pattern: str, role: str, name: str, nth: int = 0) -> list[float]

Locate an AX role + name inside a frame; return its on-page rectangle [x, y, width, height] in CSS pixels.

Same cross-frame, closed-shadow-piercing resolution as click_ax_in_frame, but returns the geometry instead of clicking — so you can drive a humanized click yourself (curved approach via dispatch_mouse_event, press at a jittered point in the box). Empty name matches any node of that role.

ax_outline_in_frame

ax_outline_in_frame(frame_url_pattern: str, depth: int | None = None) -> str

Compact accessibility outline of a specific (possibly cross-origin) frame — pierces closed shadow roots. Discover the role / accessible name to pass to click_ax_in_frame.

ax_tree_outline

ax_tree_outline(depth: int | None = None) -> str

Return the AX tree as a compact, indented role "name" outline.

Readable counterpart to get_full_ax_tree: text-noise and hidden nodes are pruned. Same output the MCP session_ax_tree tool renders.

click_ax_in_frame

click_ax_in_frame(frame_url_pattern: str, role: str, name: str, nth: int = 0, humanize: bool = False) -> None

Click an element by AX role + name inside a specific frame.

The cross-frame, shadow-piercing analogue of click_by_role: roots the AX tree at the frame matched by frame_url_pattern and descends into closed shadow roots, then clicks the match at its box-model centre with a real compositor mouse event (a trusted click, unlike a DOM .click()). Reaches widgets the page’s own JS cannot — e.g. Cloudflare Turnstile’s “Verify you are human” checkbox in a closed shadow root inside a cross-origin challenges.cloudflare.com iframe. Empty name matches any node of that role.

Cross-origin google.com / cloudflare frames must be in-process: launch the session with extra_args=["disable-site-isolation-trials"].

click_by_role

click_by_role(role: str, name: str, nth: int = 0, humanize: bool = False) -> None

Click the nth element matching accessibility role + name.

Markup-independent analogue of click_element: resolves via the AX tree, bridges to the DOM, scrolls into view, and clicks. Raises if no such node exists. Args:

  • role str — Computed accessibility role, e.g. "button", "link".
  • name str — Computed accessible name (exact match).
  • nth int — 0-based index when several nodes match.
  • humanize bool — Click at the element’s box-model centre with a humanized compositor pointer path (curved, min-jerk, tremor) instead of a DOM .click(). Off by default.

click_element

click_element(selector: str) -> None

Click the first element matching selector. Args:

  • selector str — CSS selector string.

click_xy

click_xy(x: float, y: float, humanize: bool = False) -> None

Click at (x, y) with a trusted compositor event (press → release).

With humanize=True the cursor first travels a human-like path there (see move_mouse). The programmatic analogue of the click_visual_coords MCP tool.

content

content() -> str

Return the full page HTML (document.documentElement.outerHTML).

delete_cookie(name: str, domain: str | None = None, path: str | None = None) -> None

Delete a cookie by name, optionally scoped to a domain and path. Args:

  • name str — Cookie name.
  • domain str | None — Cookie domain.
  • path str | None — Cookie path.

dispatch_key_event

dispatch_key_event(event_type: str, key: str | None = None, code: str | None = None, text: str | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchKeyEvent. Args:

  • event_type str"keyDown", "keyUp", "rawKeyDown", or "char".
  • key str | None — DOM KeyboardEvent.key value (e.g. "Enter").
  • code str | None — Physical key code (e.g. "KeyA").
  • text str | None — Character to insert (e.g. "a").
  • modifiers int | None — Bit field for modifier keys.

dispatch_mouse_event

dispatch_mouse_event(event_type: str, x: float, y: float, button: str = 'left', click_count: int = 1, delta_x: float | None = None, delta_y: float | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchMouseEvent. Args:

  • event_type str — One of "mousePressed", "mouseReleased", "mouseMoved", or "mouseWheel".
  • x float — Horizontal page coordinate.
  • y float — Vertical page coordinate.
  • button str"left", "right", or "middle".
  • click_count int — Number of clicks (usually 1).
  • delta_x float | None — Horizontal scroll delta (mouseWheel only).
  • delta_y float | None — Vertical scroll delta (mouseWheel only).
  • modifiers int | None — Bit field for modifier keys (Ctrl=1, Shift=2, etc.).

download

download(url: str, dir: str, timeout: float = 120.0, max_bytes: int | None = None) -> DownloadOutcome

Download url into directory dir; see Page.download.

eval_js

eval_js(expression: str) -> object

Alias for evaluate_js — short form used by MCP tooling.

eval_js_in_frame

eval_js_in_frame(frame_url_pattern: str, expression: str) -> object

Alias for evaluate_js_in_frame.

evaluate_js

evaluate_js(expression: str) -> object

Evaluate a JavaScript expression and return the result. Args:

  • expression str — JavaScript expression or IIFE string.

evaluate_js_in_frame

evaluate_js_in_frame(frame_url_pattern: str, expression: str) -> object

Evaluate expression inside a (possibly cross-origin) iframe.

The frame is selected by a substring of its URL. The expression runs in that frame’s own execution context (document is the frame’s document) — the way to reach an iframe whose contentDocument is null from the parent under the same-origin policy. Args:

  • frame_url_pattern str — Substring of the target frame’s URL, e.g. "recaptcha/api2/bframe".
  • expression str — JavaScript expression or IIFE string.

Raises:

  • RuntimeError — if no frame matches, or the matched frame has no scriptable execution context.

frame_urls

frame_urls() -> list[str]

List the URLs of every frame on the page, in no particular order.

Handy for discovering the right frame_url_pattern to pass to evaluate_js_in_frame.

get_cookies

get_cookies() -> list[dict[str, Any]]

Return all cookies matching the current page URL.

Each cookie is a dict with keys: name, value, domain, path, expires, size, httpOnly, secure, session, etc.

get_full_ax_tree

get_full_ax_tree(depth: int | None = None) -> list[dict[str, Any]]

Return the browser-computed accessibility (AX) tree.

Wraps CDP Accessibility.getFullAXTree. The result is a flat list of AX node dicts linked by childIds/parentId; each node carries role, computed name, properties (state), and backendDOMNodeId. Call after the page has rendered. Args:

  • depth int | None — Maximum descendant depth to traverse. None returns the whole tree.

goto

goto(url: str, timeout: float = 30.0, capture_endpoints: bool = False) -> PageResponse

Navigate to url and wait for network idle in one shot. Args:

  • url str — The URL to load.
  • timeout float — Maximum seconds to wait for network idle.
  • capture_endpoints bool — When True, record the data-plane network endpoints (XHR + Fetch request URLs) seen during the load and surface them as PageResponse.endpoints — sanitized (query/fragment/userinfo stripped, secret-like path segments redacted), deduplicated, sorted, and capped.

Returns: PageResponse — class:PageResponse with HTML, final URL, status code, PageResponse — and redirect flag.

move_mouse

move_mouse(x: float, y: float, humanize: bool = False) -> None

Move the virtual cursor to (x, y) via CDP Input.dispatchMouseEvent.

With humanize=True the cursor travels a realistic curved, minimum-jerk, lightly-tremored path (multiple MouseMoved events) from its last position; otherwise it jumps in one event. No page-world JS is injected.

navigate(url: str) -> None

Navigate to url without waiting for any load event. Args:

  • url str — The URL to load.

query_ax_tree

query_ax_tree(role: str | None = None, name: str | None = None) -> list[dict[str, Any]]

Query the AX tree (Accessibility.queryAXTree) for matching nodes.

The semantic analogue of query_selector_all: addresses by computed role / accessible name rather than markup. Name matching is exact. Passing neither returns every node under the document root.

query_selector

query_selector(selector: str) -> str | None

Return the inner HTML of the first element matching selector, or None. Args:

  • selector str — CSS selector string.

query_selector_all

query_selector_all(selector: str) -> list[str]

Return the inner HTML of every element matching selector. Args:

  • selector str — CSS selector string.

reset_download

reset_download() -> None

Reset this tab’s CDP download behavior to Chrome’s default.

screenshot_png

screenshot_png() -> bytes

Capture a full-page screenshot as PNG bytes.

set_cookie(name: str, value: str, domain: str | None = None, path: str | None = None, secure: bool | None = None, http_only: bool | None = None) -> None

Set a cookie on the current page. Args:

  • name str — Cookie name.
  • value str — Cookie value.
  • domain str | None — Cookie domain (default: current page domain).
  • path str | None — Cookie path.
  • secure bool | None — Mark as Secure.
  • http_only bool | None — Mark as HttpOnly.

set_geolocation

set_geolocation(latitude: float, longitude: float, accuracy: float | None = None) -> None

Override geolocation and grant the geolocation permission.

navigator.geolocation reads require a secure context (https / localhost), not data: URLs. accuracy defaults to 50 metres.

set_headers

set_headers(headers: dict[str, str]) -> None

Set extra HTTP headers for all subsequent requests from this tab. Args:

  • headers dict[str, str] — Header name-value pairs.

set_locale

set_locale(locale: str) -> None

Override the locale (Intl + Accept-Language), e.g. "fr-FR".

set_timezone

set_timezone(timezone_id: str) -> None

Override the timezone by IANA id, e.g. "America/New_York".

title

title() -> str | None

Return the document title, or None.

type_into

type_into(selector: str, text: str) -> None

Focus the first element matching selector and type text. Args:

  • selector str — CSS selector string.
  • text str — The text to type.

url

url() -> str | None

Return the current page URL, or None.

wait_download

wait_download(capture: DownloadCapture, timeout: float = 120.0) -> DownloadOutcome

Await an armed capture; see Page.wait_download.

wait_for_navigation

wait_for_navigation() -> None

Block until the current navigation completes.

wait_for_network_idle

wait_for_network_idle(timeout: float = 30.0) -> str | None

Wait for network activity to settle. Args:

  • timeout float — Maximum seconds to wait.

Returns: str | None"networkIdle" or "networkAlmostIdle" on success, str | NoneNone on timeout.

wait_for_selector

wait_for_selector(selector: str, timeout: float = 30.0) -> None

Wait until a CSS selector matches. Event-driven — no polling.

Raises VoidCrawlError if timeout seconds elapse without a match.

Action Framework

ActionNode

Abstract base for all browser actions.

Subclass and implement run to create a custom action. Use JsActionNode when the action can be expressed as a single JavaScript snippet; subclass ActionNode directly for CDP-level actions that need voidcrawl.actions.Tab.dispatch_mouse_event or voidcrawl.actions.Tab.dispatch_key_event.

run

run(tab: Tab) -> object

Execute this action against tab. Args:

  • tab Tab — Any object satisfying the voidcrawl.actions.Tab protocol (e.g. Page or PooledTab).

Returns: object — The action result — type varies by action.

Flow

An ordered sequence of actions executed against a single tab.

Actions run sequentially in the order added; each result is captured in the returned FlowResult. Args:

  • actions list[ActionNode] | None — Initial list of actions. May be None or omitted to start with an empty flow and use add.

add

add(action: ActionNode) -> Flow

Append an action and return self for chaining. Args:

  • action ActionNode — The action to append.

Returns: Flow — This instance, for builder-style chaining.

run

run(tab: Tab) -> FlowResult

Execute all actions sequentially against tab.

When tab is a voidcrawl.debug.DebugPage (i.e. the session was started with BrowserConfig(debug=True)), execution is automatically routed through an interactive voidcrawl.debug.DebugSession instead of running silently. Args:

  • tab Tab — Any object satisfying the Tab protocol.

Returns: FlowResult — One result per action.

FlowResult

Aggregated result of a Flow execution. Attributes:

  • results list[object] — Ordered list of return values, one per action.
  • last object — The return value of the final action, or None for empty flows (read-only property).

JsActionNode

Action executed by evaluating a JavaScript snippet in the page.

Subclasses set the js class attribute (via load_js or inline_js) and store their parameters as instance attributes in __init__. At execution time, all instance attributes are serialised to JSON and injected as the __params object visible inside the snippet.

By default params returns vars(self); override it only if you need to transform or filter attributes.

params

params() -> dict[str, Any]

Return the parameters injected as __params in the JS snippet.

Defaults to vars(self). Override to transform, rename, or filter attributes before they reach JavaScript. Returns: dict[str, Any] — A JSON-serialisable dict of parameter names to values.

run

run(tab: JsTab) -> object

Evaluate the JS snippet in tab with the current params. Args:

  • tab JsTab — Any object satisfying voidcrawl.actions.JsTab.

Returns: object — The JSON-deserialised return value from the snippet.

JsSource

Immutable wrapper around a JavaScript snippet string.

Created via load_js (file-based) or inline_js (string literal). Used as the js class attribute on JsActionNode subclasses. Args:

  • js str — Raw JavaScript source code.

JsTab

Minimal protocol for JavaScript-only actions.

Any object with an async evaluate_js method satisfies this protocol — including Page, PooledTab, and test mocks. Used by voidcrawl.actions.JsActionNode.

eval_js

eval_js(expression: str) -> object

Alias for evaluate_js — short form used by MCP tooling. Args:

  • expression str — JavaScript expression or IIFE string.

Returns: object — The JSON-deserialised return value from the browser.

evaluate_js

evaluate_js(expression: str) -> object

Evaluate a JavaScript expression in the page and return the result. Args:

  • expression str — JavaScript expression or IIFE string.

Returns: object — The JSON-deserialised return value from the browser.

Tab

Full protocol covering JS evaluation and CDP input commands.

Both Page and PooledTab satisfy this protocol. CDP-level actions (e.g. voidcrawl.actions.CdpClick) require this protocol rather than the simpler JsTab.

dispatch_key_event

dispatch_key_event(event_type: str, key: str | None = None, code: str | None = None, text: str | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchKeyEvent. Args:

  • event_type str"keyDown", "keyUp", "rawKeyDown", or "char".
  • key str | None — DOM KeyboardEvent.key value (e.g. "Enter").
  • code str | None — Physical key code (e.g. "KeyA").
  • text str | None — Character to insert (e.g. "a").
  • modifiers int | None — Bit field for modifier keys.

dispatch_mouse_event

dispatch_mouse_event(event_type: str, x: float, y: float, button: str = 'left', click_count: int = 1, delta_x: float | None = None, delta_y: float | None = None, modifiers: int | None = None) -> None

Send a low-level CDP Input.dispatchMouseEvent. Args:

  • event_type str — One of "mousePressed", "mouseReleased", "mouseMoved", or "mouseWheel".
  • x float — Horizontal page coordinate.
  • y float — Vertical page coordinate.
  • button str — Mouse button — "left", "right", or "middle".
  • click_count int — Number of clicks (usually 1).
  • delta_x float | None — Horizontal scroll delta (mouseWheel only).
  • delta_y float | None — Vertical scroll delta (mouseWheel only).
  • modifiers int | None — Bit field for modifier keys (Ctrl=1, Shift=2, etc.).

inline_js

inline_js(code: str) -> JsSource

Create a JsSource from an inline string literal. Args:

  • code str — Raw JavaScript source code.

Returns: JsSource — The wrapped JavaScript source.

load_js

load_js(path: str | Path) -> JsSource

Load JavaScript from a .js file on disk.

Absolute paths are used as-is. Relative paths are resolved from the caller’s source file, so load_js("click_at.js") works when the .js lives next to the calling .py. Args:

  • path str | Path — Filesystem path to the .js file.

Returns: JsSource — The loaded JavaScript source.

Built-in Actions

AntibotChallenge

An anti-bot vendor is actively challenging the response.

Signature-based (header/status/body), distinct from the DOM-based CaptchaDetected. Not raised on the fetch / fetch_many path — those surface the verdict as the non-fatal PageResponse.antibot annotation instead; this is reserved for explicit detect/routing callers that opt into failing on a wall.

AntibotVerdict

Signature-based anti-bot / CDN vendor fingerprint of a response. Attributes:

  • vendors list[str] — Canonical vendor tags detected (e.g. "cloudflare", "datadome"), sorted.
  • challenged boolTrue when an active wall/challenge fired (rotate), vs. mere CDN presence (no action needed).
  • challenge_vendor str | None — Vendor whose challenge fired, when challenged.
  • corpus_version str — Signature corpus the verdict was produced against — record alongside captures for replay-grade provenance.
  • evidence str — Which tier matched — "none" / "headers" / "body".

Attr

Attr(css: str, attr: str, sanitize: Callable[[str | None], str | None] | None = None) -> Any

Declare a field extracted from an element attribute.

CSS selectors are validated at class-definition time; strings containing <, >, or null bytes raise ValueError. Args:

  • css str — CSS sub-selector relative to the root element. Pass "" to target the root element itself.
  • attr str — HTML attribute name (e.g. "href", "src").
  • sanitize Callable[[str | None], str | None] | None — Optional callable applied to the extracted value before Pydantic validation. Use safe_url to block dangerous URL schemes on link/image fields.

Returns: Any — A Pydantic FieldInfo carrying the selector, attribute, and Any — sanitizer metadata.

BrowserClosedError

CaptchaDetected

DOM markers indicate a captcha / bot-wall challenge on the page.

CapturedResponse

A passively observed response with an opt-in bounded body.

bytes

bytes() -> bytes

json

json() -> Any

text

text() -> str

CdpClick

Click at (x, y) via CDP Input.dispatchMouseEvent.

Sends a mousePressed followed by mouseReleased. More realistic than JS-level clicks — useful for pages that inspect event coordinates. Args:

  • x float — Horizontal page coordinate.
  • y float — Vertical page coordinate.
  • button str — Mouse button — "left", "right", or "middle".

run

run(tab: Tab) -> None

Dispatch mousePressed then mouseReleased at (x, y). Args:

  • tab Tab — Tab-like object to send the click events to.

CdpClickAndHold

Mouse-down, hold for duration_ms, then mouse-up via CDP.

Useful for triggering long-press menus or drag initialisation. Args:

  • x float — Horizontal page coordinate.
  • y float — Vertical page coordinate.
  • duration_ms int — How long to hold the button, in milliseconds.
  • button str — Mouse button — "left", "right", or "middle".

run

run(tab: Tab) -> None

Press, hold for duration_ms, then release at (x, y). Args:

  • tab Tab — Tab-like object to send the mouse events to.

CdpHover

Move the virtual mouse cursor to (x, y) via CDP.

Sends a mouseMoved event. Unlike Hover, this moves the actual CDP cursor position, which is needed for subsequent CdpClick calls to land correctly. Args:

  • x float — Horizontal page coordinate.
  • y float — Vertical page coordinate.

run

run(tab: Tab) -> None

Dispatch a mouseMoved event to (x, y). Args:

  • tab Tab — Tab-like object to send the hover event to.

CdpScroll

Scroll via a CDP mouseWheel event fired at (x, y).

For most use cases prefer the convenience subclasses CdpScrollDown, CdpScrollUp, CdpScrollLeft, and CdpScrollRight. Args:

  • x float — Horizontal page coordinate for the wheel event origin.
  • y float — Vertical page coordinate for the wheel event origin.
  • delta_x float — Horizontal scroll amount (positive = right).
  • delta_y float — Vertical scroll amount (positive = down).

run

run(tab: Tab) -> None

Dispatch a mouseWheel event at (x, y). Args:

  • tab Tab — Tab-like object to send the scroll event to.

CdpScrollDown

Scroll down by pixels at (x, y) via CDP. Args:

  • pixels float — Distance to scroll in pixels. Defaults to 100.
  • x float — Horizontal origin for the wheel event.
  • y float — Vertical origin for the wheel event.

CdpScrollLeft

Scroll left by pixels at (x, y) via CDP. Args:

  • pixels float — Distance to scroll in pixels. Defaults to 100.
  • x float — Horizontal origin for the wheel event.
  • y float — Vertical origin for the wheel event.

CdpScrollRight

Scroll right by pixels at (x, y) via CDP. Args:

  • pixels float — Distance to scroll in pixels. Defaults to 100.
  • x float — Horizontal origin for the wheel event.
  • y float — Vertical origin for the wheel event.

CdpScrollUp

Scroll up by pixels at (x, y) via CDP. Args:

  • pixels float — Distance to scroll in pixels. Defaults to 100.
  • x float — Horizontal origin for the wheel event.
  • y float — Vertical origin for the wheel event.

CdpTypeText

Type text character-by-character via CDP Input.dispatchKeyEvent.

Each character produces a keyDown/keyUp pair. This is more realistic than SetInputValue and triggers per-keystroke event listeners. Args:

  • text str — The string to type.

run

run(tab: Tab) -> None

Dispatch keyDown/keyUp pairs for each character in text. Args:

  • tab Tab — Tab-like object to send the key events to.

ChromeProfileBusy

Chrome’s own SingletonLock prevented profile launch.

ClearInput

Clear an input field and fire an input event via JS. Args:

  • selector str — CSS selector targeting the <input> or <textarea>.

ClickAt

Click the element at page coordinates (x, y) via JS events.

Uses document.elementFromPoint to resolve the target and dispatches synthetic mouse events. Args:

  • x int — Horizontal page coordinate (pixels from left).
  • y int — Vertical page coordinate (pixels from top).

ClickElement

Click the first element matching a CSS selector via JS.

Raises a JS Error if no element matches. Args:

  • selector str — CSS selector string (e.g. "#submit-btn").

CollectNetworkRequests

Retrieve all network entries captured by InstallNetworkObserver.

Returns a list of dicts, each containing:

  • name — the full URL of the request
  • type — the initiator type ("script", "img", "fetch", etc.)
  • duration — round-trip time in milliseconds
  • size — transfer size in bytes (0 if unavailable)

Pass clear=True to reset the log after collection.

GetAttribute

Read an HTML attribute from the first matching element.

Returns None if the element is not found. The result is available as the return value of run (str | None). Args:

  • selector str — CSS selector targeting the element.
  • attr str — Attribute name (e.g. "href", "data-id").

run

run(tab: JsTab) -> str | None

GetText

Read textContent from the first matching element.

Returns None if the element is not found. The result is available as the return value of run (str | None). Args:

  • selector str — CSS selector targeting the element.

run

run(tab: JsTab) -> str | None

Hover

Dispatch mouseenter + mouseover on an element via JS.

Triggers CSS :hover styles and JS hover handlers without moving the CDP-level cursor. Args:

  • selector str — CSS selector targeting the element to hover.

InstallNetworkObserver

Install a PerformanceObserver that records network requests.

Call this after navigation completes (e.g. after wait_for_network_idle). The observer uses buffered: true to retroactively capture all resource entries from the current navigation, plus any future requests.

Use CollectNetworkRequests to retrieve the captured entries.

The observer stores entries on window.__vc_network_log.

params

params() -> dict[str, object]

ManagedProfileSnapshot

close

close() -> None

ManagedProfileSplit

close

close() -> None

PageResponse

Result of Page.goto / PooledTab.goto. Attributes:

  • html str — Full outer HTML after network idle.
  • url str — Final URL after any redirects.
  • status_code int | None — HTTP status of the last response, or None when served from cache / service worker.
  • redirected boolTrue when at least one HTTP redirect occurred.
  • headers dict[str, str] — Final Document response headers (lowercased names; last value wins on duplicates). Empty when no network response was captured.
  • antibot AntibotVerdict | None — Anti-bot / CDN vendor fingerprint, or None when no network response was captured.
  • endpoints list[str] | None — Data-plane network endpoints (XHR + Fetch request URLs) — a sorted, deduplicated set of scheme://host/path with query, fragment, and userinfo stripped and secret-like path segments redacted at the source. None unless capture_endpoints=True was passed to goto; [] when requested but none were seen.
  • endpoints_truncated boolTrue when the endpoint set hit its cap and further endpoints were dropped.
  • endpoint_sanitizer_version str | None — Which redaction-rule version produced endpoints (record it alongside the set for replay-grade provenance). None iff endpoints is None.

ProfileBusy

Another voidcrawl process holds the profile lock (non-blocking acquire).

ProfileHandle

Live lease on a Chrome profile.

Use as an async context manager, or call release explicitly. Obtain one via voidcrawl.acquire_profile or voidcrawl.with_profile.

new_page

new_page(url: str) -> Page

path

path() -> str

release

release() -> None

ProfileLeaseExpired

Timed out waiting for the profile lock.

ProfileNotFound

No matching profile directory in the platform default dirs.

ProfileRegistry

VoidCrawl-managed standalone Chromium profile registry.

Profiles live under $VOIDCRAWL_PROFILE_ROOT or the platform data-dir default, and each profile path is a standalone Chrome user_data_dir. Methods return metadata only; cookie and localStorage values are never read.

clone_profile

clone_profile(source_id_or_path: str, id: str, description: str | None = None, labels: tuple[str, ...] | list[str] = ()) -> dict[str, object]

create_pool

create_pool(name: str, profile_ids: list[str] | tuple[str, ...], max_active: int = 3) -> dict[str, object]

create_profile

create_profile(id: str, description: str | None = None, labels: tuple[str, ...] | list[str] = ()) -> dict[str, object]

default

default() -> ProfileRegistry

delete_profile

delete_profile(id: str) -> bool

describe_profile

describe_profile(id: str) -> dict[str, object]

fork_profile

fork_profile(source: str = 'Default', copies: int = 2) -> ManagedProfileSplit

Fork a closed native Chrome profile into concurrent instances.

source may be a discovered profile name such as "Default" or an explicit native profile-directory path. The source Chrome must be closed so its cookie databases and storage are quiescent. Each result is a temporary standalone user_data_dir containing the selected profile as Default plus Chrome’s root Local State metadata.

The copies start with the same login state and profile identity, then intentionally diverge. They are deleted when the async context exits.

list_pools

list_pools() -> list[dict[str, object]]

list_profiles

list_profiles() -> list[dict[str, object]]

resolve_pool

resolve_pool(name: str) -> dict[str, object]

snapshot_profile

snapshot_profile(id: str) -> ManagedProfileSnapshot

Create a quiesced, temporary clone that cleans itself up.

Use the returned object as an async context manager. The source’s OS lease is held while copying; lock and Chrome Singleton files are excluded from the snapshot.

split_profile

split_profile(id: str, copies: int = 2) -> ManagedProfileSplit

Prepare isolated copies of one profile for concurrent Chrome instances.

The returned async context manager performs the copy work off the asyncio thread. It takes one source lease across the complete split, so every copy starts from the same quiesced baseline. Each path is a separate Chrome user_data_dir and can therefore run concurrently without a SingletonLock conflict.

This is copy-on-start isolation, not live synchronization: cookies, storage, and other writes diverge once the Chrome instances launch and are not merged back into the source.

Example::

async with registry.split_profile("work", copies=2) as split:
first_path, second_path = split.paths
# Launch one BrowserSession per path.

QueryAll

Query all elements matching selector and extract fields from each.

Each entry in fields maps a result key to either:

  • a CSS sub-selector string — the matched element’s textContent (trimmed) is returned, or None if nothing matches.
  • a (sub_selector, attr) tuple — the named attribute of the matched element is returned, or None if nothing matches.

Pass an empty string "" as the sub-selector to target the root element itself rather than a descendant.

Pass a voidcrawl.schema.Schema subclass as fields to receive typed model instances instead of raw dicts. Args:

  • selector str — CSS selector for the root elements to iterate over.
  • fields dict[str, str | tuple[str, str]] | type[Schema] — Mapping of result key → sub-selector or (sub_selector, attribute) tuple, or a voidcrawl.schema.Schema subclass whose field declarations are used automatically.

Example — raw dicts::

QueryAll(
".article",
{"title": "h2", "url": ("a", "href"), "date": ".byline"},
)

Example — typed Schema::

class Article(vc.Schema):
title: str = vc.Text("h2")
url: str | None = vc.Attr("a", "href")
QueryAll(".article", Article)

params

params() -> dict[str, Any]

run

run(tab: JsTab) -> list[_T]

ResponseExpectation

Async context returned by Page.expect_response(s).

ResponseTimeoutError

ScaleReport

Computed pool recommendations for a profile and resource snapshot.

print_report() -> None

Print a human-readable summary. Uses rich markup if available.

to_dict

to_dict() -> dict[str, object]

Serialise to a JSON-compatible dict (for --json scripted mode).

to_pool_config

to_pool_config() -> PoolConfig

Convert to a voidcrawl.PoolConfig ready for voidcrawl.BrowserPool.

Schema

Base class for declarative DOM extraction models.

Subclass this and annotate fields with Text or Attr to declare how each field is extracted from the DOM. Pass the subclass to voidcrawl.actions.QueryAll to receive typed instances instead of raw dicts.

Example::

class Article(Schema):
headline: str = Text("h2")
url: str | None = Attr("a", "href", sanitize=safe_url)
excerpt: str | None = Text(".summary", sanitize=strip_tags)

ScrollBy

Scroll the window by a relative offset via window.scrollBy. Args:

  • dx int — Horizontal delta in pixels (positive = right). Defaults to 0.
  • dy int — Vertical delta in pixels (positive = down). Defaults to 0.

ScrollTo

Scroll the window to an absolute position via window.scrollTo. Args:

  • x int — Horizontal scroll offset in pixels. Defaults to 0.
  • y int — Vertical scroll offset in pixels. Defaults to 0.

SelectOption

Select a <select> option by value and fire a change event. Args:

  • selector str — CSS selector targeting the <select> element.
  • value str — The value attribute of the <option> to select.

SetAttribute

Set an HTML attribute on the first matching element.

Raises a JS Error if no element matches the selector. Args:

  • selector str — CSS selector targeting the element.
  • attr str — Attribute name to set.
  • value str — Attribute value to assign.

run

run(tab: JsTab) -> None

SetInputValue

Bulk-set an input’s value and fire input/change events.

This does not simulate individual keystrokes — use CdpTypeText for realistic per-character typing. Args:

  • selector str — CSS selector targeting the <input> or <textarea>.
  • text str — The value to assign.

Text

Text(css: str, sanitize: Callable[[str | None], str | None] | None = None) -> Any

Declare a field extracted from an element’s textContent.

The css string is a sub-selector relative to the root element matched by voidcrawl.actions.QueryAll’s selector argument. Pass "" to target the root element itself.

CSS selectors are validated at class-definition time; strings containing <, >, or null bytes raise ValueError. Args:

  • css str — CSS sub-selector whose textContent (trimmed) is used as the field value. None is returned when no element matches.
  • sanitize Callable[[str | None], str | None] | None — Optional callable applied to the extracted value before Pydantic validation. Use safe_url or strip_tags, or supply your own (str | None) -> str | None function.

Returns: Any — A Pydantic FieldInfo carrying the selector and sanitizer Any — metadata.

VoidCrawlError

Base class for all voidcrawl errors raised from the native extension.

WaitForSelector

Poll until a CSS selector matches an element, with timeout.

Polls at a short interval inside the browser context. Resolves as soon as document.querySelector(selector) is non-null, or throws a JS Error when timeout seconds elapse. Args:

  • selector str — CSS selector to wait for.
  • timeout float — Maximum wait time in seconds. Defaults to 10.0.

WaitForTimeout

Sleep for ms milliseconds inside the browser context.

This pauses the JS execution inside the page, not the Python event loop. Useful for waiting on animations or debounced handlers. Args:

  • ms int — Delay in milliseconds.

acquire_profile

acquire_profile(name: str, lease_timeout: float = 300.0, headless: bool = True) -> ProfileHandle

Acquire an exclusive lease on a Chrome profile. Args:

  • name str — Profile directory name as Chrome stores it (e.g. "Default", "Profile 1").
  • lease_timeout float — Seconds to poll for the lock before giving up. 0 means fail immediately if busy.
  • headless bool — Run Chrome in headless mode (default). Set False for a visible window — useful for a one-time manual login before the profile is used for scraping.

Raises:

  • ProfileBusy — Another voidcrawl process holds the lock and the timeout is zero.
  • ProfileLeaseExpired — Timed out waiting for the lock.
  • ProfileNotFound — No matching profile directory in the platform default dirs.

list_profiles

list_profiles() -> list[tuple[str, str]]

Return [(name, path), ...] for every Chrome profile discovered. Only directories containing a Preferences file are returned.

safe_url

safe_url(value: str | None) -> str | None

Return None for javascript:, data:, and vbscript: URLs.

Use this on any Attr field that extracts a URL (href, src, action, etc.) to prevent unsafe schemes from propagating. Args:

  • value str | None — Raw attribute value from the DOM, or None.

Returns: str | None — The original value, or None if a dangerous scheme was detected.

strip_tags

strip_tags(value: str | None) -> str | None

Strip HTML tag-like substrings from value.

Useful when a field may contain inline markup — removes anything that looks like <tag> or </tag> using a simple regex. Not a full HTML sanitiser; use on plain-text fields that shouldn’t contain markup. Args:

  • value str | None — Raw text value from the DOM, or None.

Returns: str | None — The value with tag-like substrings removed, or None unchanged.

with_profile

with_profile(name: str, lease_timeout: float = 300.0, headless: bool = True) -> AsyncIterator[ProfileHandle]

Async context manager: acquire, yield, release.

Example::

async with with_profile("Default") as handle:
page = await handle.new_page("https://linkedin.com/in/me")
html = await page.content()

Pass headless=False to see the Chrome window (e.g. for a manual login flow).

Debug

DebugSession

Interactive step debugger for browser actions.

Queue actions via add (or add_flow), then call start to execute them with an interactive debug control. Args:

  • tab Tab — The page or pooled tab to run actions against.
  • start_url str | None — URL to navigate to when rewinding. Required for back/restart — if omitted those commands are disabled.
  • stepping bool — If True (default), pause before every action. If False, run freely and only pause at breakpoints.
  • step_delay float — Seconds to wait after each action in non-stepping mode (ignored when paused at a prompt). Defaults to 0.3.
  • highlight bool — Flash a CSS outline on selector-targeted elements before executing the action. Defaults to True.
  • nav_settle_secs float — Seconds to wait after triggering a rewind navigation before replaying actions. Increase if pages are slow to load. Defaults to 0.5.

add

add(action: ActionNode) -> DebugSession

Append a single action to the execution queue. Args:

  • action ActionNode — The action to enqueue.

Returns: DebugSession — This session, for chaining.

add_flow

add_flow(flow: Flow) -> DebugSession

Append every action from a Flow to the queue. Args:

  • flow Flow — The flow whose actions to enqueue.

Returns: DebugSession — This session, for chaining.

start

start() -> FlowResult

Run the queued actions with interactive debug control.

Prints a command prompt before each action (or only at breakpoints, depending on stepping). The user can type:

  • n / Enter — execute the current action and advance
  • c — continue running until the next breakpoint
  • b — rewind one step (re-navigates and replays)
  • r — restart from the beginning
  • l — list all queued actions with position marker
  • h — show history of executed actions and results
  • q — quit the session early Returns: FlowResult — Aggregated results collected so far.

vc_breakpoint

vc_breakpoint() -> _T

Mark an action class as a debugger breakpoint.

When a DebugSession encounters an action whose class is marked with this decorator, it pauses execution regardless of whether stepping mode is active. Args:

  • cls _T — The action class to mark.

Returns: _T — The same class, with an internal marker attribute set.