Example: JavaScript Eval
Run JavaScript expressions inside the browser and get results back as native Python types.
Code
import asyncio
from voidcrawl import BrowserPool, PoolConfig
async def main() -> None: async with BrowserPool(PoolConfig()) as pool, pool.acquire() as tab: await tab.goto("https://qscrape.dev")
# evaluate_js returns native Python types user_agent = await tab.evaluate_js("navigator.userAgent") print(f"User agent: {user_agent}")
# Compute something in-page p_count = await tab.evaluate_js( "document.querySelectorAll('p').length" ) print(f"Number of <p> tags: {p_count}")
# Return structured data dims = await tab.evaluate_js( "({w: window.innerWidth, h: window.innerHeight})" ) print(f"Viewport: {dims}")
# Modify the DOM via JS await tab.evaluate_js("document.title = 'Modified by voidcrawl'") print(f"New title: {await tab.title()}")
if __name__ == "__main__": asyncio.run(main())Key Points
evaluate_js()accepts any JavaScript expression and returns the result as a native Python type:str,int,float,bool,dict,list, orNone.- To return an object literal, wrap it in parentheses:
({key: value}). Without parens, JS interprets{as a block statement. - You can modify the DOM, read browser APIs, and compute values — anything you can do in a browser console.
Reaching cross-origin iframes
evaluate_js() runs in the top document, so it cannot read a cross-origin
iframe — the same-origin policy makes that frame’s contentDocument return
null. Use evaluate_js_in_frame(url_pattern, expression) to run the
expression inside the target frame’s own execution context, where document
is that frame’s document. The frame is selected by a substring of its URL
(frame_urls() lists what is available). This reaches payment iframes, OAuth
frames, consent walls, and reCAPTCHA’s bframe.
# Read a value the parent page is blocked from seeing:inside = await tab.evaluate_js_in_frame( "checkout.stripe.com", "document.querySelector('#card-errors')?.textContent")
# Discover frame URLs if you are not sure of the pattern:for url in await tab.frame_urls(): print(url)The match must be unique: if more than one frame’s URL contains the pattern,
the call fails closed rather than guessing — use a specific substring (e.g.
recaptcha/api2/bframe, not recaptcha).
In-process requirement. The target frame must be in the page’s renderer
process. VoidCrawl’s defaults keep ordinary cross-origin frames in-process, but
Chrome field-trial-isolates a few origins (notably google.com, so reCAPTCHA)
out-of-process. To reach those, launch with
BrowserConfig(extra_args=["disable-site-isolation-trials"]) — an explicit
opt-in, since it weakens the browser’s isolation posture.
Expected Output
User agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ...Number of <p> tags: 2Viewport: {'w': 1920, 'h': 1080}New title: Modified by voidcrawl