Skip to content
Cascading Labs QScrape VoidCrawl Yosoi

Built-in Actions

VoidCrawl provides two tiers of browser actions:

  • JS-tier — actions evaluated as JavaScript inside the page (DOM manipulation, reading attributes, scrolling)
  • CDP-tier — actions that call Chrome DevTools Protocol input methods directly (mouse events, keyboard events)

Both tiers can be used standalone or composed into Flows.

Using Actions

Actions are classes you instantiate and run() against a tab or page:

from voidcrawl.actions import ClickElement, GetText, SetInputValue
async with pool.acquire() as tab:
await tab.goto("https://qscrape.dev") # waits for network idle
# Each action is instantiated with parameters, then run against a tab
await SetInputValue("#search", "hello").run(tab)
await ClickElement("#submit").run(tab)
title = await GetText("h1").run(tab)

JS-Tier Actions (DOM)

These actions execute JavaScript inside the page context. They work with CSS selectors and return Python-native values.

Click

ActionParametersReturnsDescription
ClickElementselector: strNoneClick the first element matching the CSS selector
ClickAtx: int, y: intNoneClick at specific page coordinates (via JS)
from voidcrawl.actions import ClickElement, ClickAt
await ClickElement("#submit-btn").run(tab)
await ClickAt(100, 200).run(tab)

Input

ActionParametersReturnsDescription
SetInputValueselector: str, value: strNoneSet the value of an input element
ClearInputselector: strNoneClear an input element
SelectOptionselector: str, value: strNoneSelect an option in a <select> element
from voidcrawl.actions import SetInputValue, ClearInput, SelectOption
await SetInputValue("#name", "World").run(tab)
await ClearInput("#name").run(tab)
await SelectOption("#country", "US").run(tab)

DOM Queries

ActionParametersReturnsDescription
GetTextselector: strstrGet the text content of an element
GetAttributeselector: str, attr: strstr | NoneGet an attribute value
SetAttributeselector: str, attr: str, value: strNoneSet an attribute value
from voidcrawl.actions import GetText, GetAttribute
title = await GetText("h1").run(tab)
href = await GetAttribute("a.logo", "href").run(tab)

Scroll

ActionParametersReturnsDescription
ScrollTox: int, y: intNoneScroll to absolute position
ScrollBydx: int, dy: intNoneScroll by relative offset
from voidcrawl.actions import ScrollTo, ScrollBy
await ScrollTo(0, 0).run(tab) # scroll to top
await ScrollBy(0, 500).run(tab) # scroll down 500px

Hover

ActionParametersReturnsDescription
Hoverselector: strNoneHover over an element (triggers CSS :hover and JS events)

Wait

ActionParametersReturnsDescription
WaitForSelectorselector: str, timeout: floatboolWait for an element to appear
WaitForTimeoutms: floatNoneWait for a fixed duration
from voidcrawl.actions import WaitForSelector
found = await WaitForSelector("#content", timeout=5.0).run(tab)
if not found:
print("Element did not appear within 5 seconds")

Network

ActionParametersReturnsDescription
InstallNetworkObserver(none)NoneInstall a PerformanceObserver that records all network requests. Uses buffered: true to capture past entries.
CollectNetworkRequestsclear: boollist[dict]Retrieve captured network entries (name, type, duration, size).
from voidcrawl.actions import (
InstallNetworkObserver,
CollectNetworkRequests,
)
# Navigate, then install observer (buffered: true picks up past entries)
await tab.goto("https://qscrape.dev")
await InstallNetworkObserver().run(tab)
# Collect and clear the log
requests = await CollectNetworkRequests(clear=True).run(tab)
for r in requests:
print(f" {r['type']:>10} {r['name']}")

See Cookbook: Network logging for more detailed examples.

CDP-Tier Actions

These actions bypass JavaScript entirely and dispatch input events at the Chrome protocol level. Use them when you need low-level control or when JS-tier actions are blocked by the page.

ActionParametersDescription
CdpClickx: float, y: floatMouse click at coordinates
CdpClickAndHoldx: float, y: float, duration: floatClick and hold
CdpHoverx: float, y: floatMove mouse to coordinates
CdpTypeTexttext: strType text via key events
CdpScrollx: float, y: float, dx: float, dy: floatScroll at position
CdpScrollUp/Down/Left/Rightamount: floatDirectional scroll
from voidcrawl.actions import CdpClick, CdpTypeText
await CdpClick(100.0, 200.0).run(tab)
await CdpTypeText("hello world").run(tab)

When to Use Which Tier

ScenarioUse
Fill a form, click a buttonJS-tier (SetInputValue, ClickElement)
Read text or attributes from the DOMJS-tier (GetText, GetAttribute)
Bypass click interception or overlaysCDP-tier (CdpClick)
Simulate realistic mouse movementCDP-tier (CdpHover, CdpClick)
Interact with canvas or WebGLCDP-tier
Page blocks element.click()CDP-tier

Direct Page Methods

You don’t always need the actions framework. Page and PooledTab have built-in methods for common operations:

async with pool.acquire() as tab:
await tab.goto(url)
# DOM queries
html = await tab.query_selector("#main")
items = await tab.query_selector_all(".item")
# Interaction
await tab.click_element("#btn")
await tab.type_into("#input", "text")
# Cookies
await tab.set_cookie("key", "value", secure=True)
cookies = await tab.get_cookies()
await tab.delete_cookie("key")
# JS evaluation
result = await tab.evaluate_js("document.title")

The actions framework is most useful when you need to compose multiple steps or create reusable sequences. See Custom JS Actions for building your own.

FAQs

What’s the difference between ClickElement and CdpClick?

ClickElement finds an element by CSS selector and calls element.click() in JavaScript. CdpClick dispatches a mousePressed + mouseReleased event at specific x/y coordinates via the Chrome protocol. The CDP version bypasses JS event listeners and click interception.

Can I chain multiple actions?

Yes, use Flow. See Custom JS Actions for details.