FastCDP API

Source and API details
len(_cdp_domains), [d['domain'] for d in _cdp_domains[:5]]
(55, ['Accessibility', 'Animation', 'Audits', 'Autofill', 'BackgroundService'])

source

cdp_conninfo


def cdp_conninfo(
    p:NoneType=None
):

Call self as a function.

On MacOS, the connection info is stored in ~/Library/Application Support/Google/Chrome/DevToolsActivePort.

conninfo = cdp_conninfo()
conninfo
'9222/devtools/browser/6c364cd1-dae4-4e9e-857d-80fc243dbc61'

source

CDP


def CDP(
    wsconn:NoneType=None, debug:bool=False
):

Chrome DevTools Protocol connection with event support

# await cdp.close()

source

CDP.remote


async def remote(
    port:int=9222, debug:NoneType=None
):

Connect via Chrome remote debugging HTTP endpoint

cdp = await CDP.remote()

source

CDP.pages


async def pages(
    
):

Call self as a function.

ps = await cdp.pages
pg = first(p for p in ps if 'example.com' in p['url'])
pg['title']
'Example Domain'

source

CDPDomain


def CDPDomain(
    cdp, domain
):

Initialize self. See help(type(self)) for accurate signature.


source

CDPMethod


def CDPMethod(
    cdp, domain, method
):

Initialize self. See help(type(self)) for accurate signature.


source

CDP.eval


async def eval(
    expr, sid:NoneType=None
):

Call self as a function.


source

CDP.attach


async def attach(
    tid
):

Call self as a function.

tid = pg['targetId']
sid = await cdp.attach(tid)
await cdp.eval('document.title', sid)
'Example Domain'

source

CDP.wait_event


async def wait_event(
    event, timeout:int=10
):

Call self as a function.


on


def on(
    event
):

Call self as a function.


source

CDP.wait_for_selector


async def wait_for_selector(
    sel, sid:NoneType=None, timeout:int=10
):

Wait for CSS selector to match an element


source

CDP.wait_for


async def wait_for(
    expr, sid:NoneType=None, timeout:int=10
):

Wait for JS expression to be truthy, return its value


source

CDP.wait_load


async def wait_load(
    sid:NoneType=None, timeout:int=10
):

Call self as a function.

t = await cdp.target.createTarget(url='about:blank')
sid = await cdp.attach(t)
page = await cdp.page.enable(sid=sid)

await cdp.page.navigate(sid=sid, url='https://httpbin.org/forms/post')
await cdp.wait_for_selector('form', sid)
True
await cdp.target.closeTarget(targetId=t)
True

source

PageDomain


def PageDomain(
    sid, domain
):

Initialize self. See help(type(self)) for accurate signature.


source

Page


def Page(
    cdp, t, sid, owned:bool=False
):

Initialize self. See help(type(self)) for accurate signature.


source

CDP.remote_page


async def remote_page(
    port:int=9222, debug:NoneType=None
):

Connect via remote debugging and return Page for the active tab


source

CDP.active_page


async def active_page(
    
):

Call self as a function.

page = await CDP.remote_page()
await page.eval('document.title')
'l:00_core←nbs←fastcdp←aai-ws - SolveIT'

source

CDP.new_page


async def new_page(
    
):

Create a new tab, return Page

page = await cdp.new_page()
await page.page.navigate(url='https://httpbin.org/forms/post')
await page.wait_for_selector('form')
True
page.is_open
True
await page.close()
page.is_open
True

source

CDP.goto


async def goto(
    url, sid:NoneType=None, kwargs:VAR_KEYWORD
):

Navigate to url and wait for load+idle


wait_ready


def wait_ready(
    sid:NoneType=None, timeout:int=10, idle_ms:int=500
):

Context manager: subscribes before action, waits for load+idle after


source

CDP.wait_for_ready


async def wait_for_ready(
    sid:NoneType=None, timeout:int=10, idle_ms:int=500
):

Wait until network is idle for idle_ms

page = await Page.new(cdp=cdp)
await page.goto('https://httpbin.org/forms/post')

source

CDP.screenshot


async def screenshot(
    sid:NoneType=None
):

Call self as a function.

await asyncio.sleep(0.2)
img = await page.screenshot()
# img
await page.close()
await cdp.close()

LLMs and accessibility

cdp = await CDP.remote()
page = await cdp.new_page()
await page.goto('https://httpbin.org/forms/post')
await page.eval('document.title')
''
await page.accessibility.enable()
tree = await page.accessibility.getFullAXTree()
len(tree)
77
tree[0]
{'nodeId': '69',
 'ignored': False,
 'role': {'type': 'internalRole', 'value': 'RootWebArea'},
 'chromeRole': {'type': 'internalRole', 'value': 144},
 'name': {'type': 'computedString',
  'value': '',
  'sources': [{'type': 'relatedElement', 'attribute': 'aria-labelledby'},
   {'type': 'attribute', 'attribute': 'aria-label'},
   {'type': 'attribute', 'attribute': 'aria-label', 'superseded': True},
   {'type': 'relatedElement', 'nativeSource': 'title'}]},
 'properties': [{'name': 'focusable',
   'value': {'type': 'booleanOrUndefined', 'value': True}},
  {'name': 'focused', 'value': {'type': 'booleanOrUndefined', 'value': True}},
  {'name': 'url',
   'value': {'type': 'string', 'value': 'https://httpbin.org/forms/post'}}],
 'childIds': ['71'],
 'backendDOMNodeId': 69,
 'frameId': '523C62BB46692DDED9606FB1D00591B0'}
await page.close()

source

AXTree


def AXTree(
    raw
):

Chrome accessibility tree node with compact repr


source

AXNode


def AXNode(
    raw
):

Chrome accessibility tree node with compact repr


source

build_ax_tree


def build_ax_tree(
    nodes
):

Build AXNode tree from flat CDP accessibility node list


source

CDP.ax_tree


async def ax_tree(
    sid:NoneType=None
):

Get accessibility tree for session

page = await cdp.new_page()
await page.goto('https://httpbin.org/forms/post')
root = await page.ax_tree()
root
  • RootWebArea “” focusable=True url=https://httpbin.org/forms/post [#68]
    • LabelText “” [#75]
      • StaticText “Customer name:” [#37]
      • textbox “Customer name:” focusable=True editable=plaintext settable=True [#36]
    • LabelText “” [#78]
      • StaticText “Telephone:” [#38]
      • textbox “Telephone:” focusable=True editable=plaintext settable=True [#57]
    • LabelText “” [#81]
      • StaticText “E-mail address:” [#39]
      • textbox “E-mail address:” focusable=True editable=plaintext settable=True [#58]
    • group “Pizza Size” [#83]
      • Legend “” [#84]
        • StaticText “Pizza Size” [#40]
      • radio ” Small” focusable=True [#60]
      • radio ” Medium” focusable=True [#61]
      • radio ” Large” focusable=True [#62]
    • group “Pizza Toppings” [#91]
      • Legend “” [#92]
        • StaticText “Pizza Toppings” [#44]
      • checkbox ” Bacon” focusable=True [#63]
      • checkbox ” Extra Cheese” focusable=True [#64]
      • checkbox ” Onion” focusable=True [#65]
      • checkbox ” Mushroom” focusable=True [#66]
    • LabelText “” [#102]
      • StaticText “Preferred delivery time:” [#54]
      • InputTime “Preferred delivery time:” focusable=True settable=True [#67]
        • spinbutton “Hours Hours” focusable=True settable=True valuemin=1 valuemax=12 [#106]
          • StaticText “–” [#49]
        • StaticText “:” [#50]
        • spinbutton “Minutes Minutes” focusable=True settable=True valuemax=59 [#108]
          • StaticText “–” [#51]
        • StaticText ” ” [#52]
        • spinbutton “AM/PM AM/PM” focusable=True settable=True valuemin=1 valuemax=2 [#110]
          • StaticText “–” [#53]
        • button “Show time picker” focusable=True hasPopup=menu [#69]
    • LabelText “” [#112]
      • StaticText “Delivery instructions:” [#55]
      • textbox “Delivery instructions:” focusable=True editable=plaintext settable=True multiline=True [#59]
    • button “Submit order” focusable=True [#115]
      • StaticText “Submit order” [#56]

source

AXNode.find_all


def find_all(
    role:NoneType=None, name:NoneType=None
):

Find all descendants matching role and/or name substring


source

AXNode.find_id


def find_id(
    role:NoneType=None, name:NoneType=None
):

Find first descendant matching role and/or name substring


source

AXNode.find


def find(
    role:NoneType=None, name:NoneType=None
):

Find first descendant matching role and/or name substring

nmid = root.find_id('textbox', 'Customer name')
phid = root.find_id('textbox', 'Telephone')
nmid
36

source

CDP.click


async def click(
    backendNodeId, sid:NoneType=None
):

Call self as a function.


source

CDP.js_node_run


async def js_node_run(
    code, backendNodeId, sid:NoneType=None
):

Call self as a function.


source

CDP.js_node


async def js_node(
    fn, backendNodeId, sid:NoneType=None
):

Call self as a function.

await page.DOM.focus(backendNodeId=nmid)
await page.input.insertText(text='Jeremy Howard')

await page.DOM.focus(backendNodeId=phid)
await page.input.insertText(text='555-1234')
{}
await page.click(root.find_id('radio', 'Large'))
await page.click(root.find_id('checkbox', 'Extra Cheese'))

source

CDP.fill_text


async def fill_text(
    backendNodeId, text, sid:NoneType=None
):

Call self as a function.

await page.fill_text(root.find_id('textbox', 'Delivery'), 'Ring the doorbell twice')
await page.js_node_run('this.value = "18:30"', root.find_id('InputTime', 'delivery time'))
{'type': 'undefined'}

source

CDP.click_and_wait


async def click_and_wait(
    backendNodeId, sid:NoneType=None, kwargs:VAR_KEYWORD
):

Click element and wait for load+idle

await page.click_and_wait(root.find_id('button', 'Submit order'))

source

cdp_yolo


def cdp_yolo(
    
):

Allow all CDP classes in safepyrun