len(_cdp_domains), [d['domain'] for d in _cdp_domains[:5]]
(55, ['Accessibility', 'Animation', 'Audits', 'Autofill', 'BackgroundService'])
source
cdp_search
Search CDP domains and commands by name or description
cdp_search('target')[:100]
'Audits.checkFormsIssues: Runs the form issues check for the target page. Found issues are reported\nu'
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
source
CDP.remote
async def remote(
port:int=9222, debug:NoneType=None
):
Connect via Chrome remote debugging HTTP endpoint
source
CDP.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']
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
Call self as a function.
tid = pg['targetId']
sid = await cdp.attach(tid)
await cdp.eval('document.title', sid)
source
CDP.wait_event
async def wait_event(
event, timeout:int=10
):
Call self as a function.
on
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)
await cdp.target.closeTarget(targetId=t)
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
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')
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.
img = await page.screenshot()
# img
LLMs and accessibility
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)
{'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'}
source
AXTree
Chrome accessibility tree node with compact repr
source
AXNode
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 “:” [#50]
- spinbutton “Minutes Minutes”
focusable=True settable=True valuemax=59 [#108]
- StaticText ” ” [#52]
- spinbutton “AM/PM AM/PM”
focusable=True settable=True valuemin=1 valuemax=2 [#110]
- 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
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'))
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
Allow all CDP classes in safepyrun