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 (or ~/Library/Application Support/Chromium/DevToolsActivePort for Chromium).
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