Package: aio-dt-protocol
Асинхронная обёртка надпротоколомотладчика браузера Chromium.Запуски проводятся только в ОС Windows и Linux.Имеет одну зависимость:https://github.com/aaugustin/websocketsУстановкаpipinstallaio-dt-protocolПримеры:importasynciofromaio_dt_protocolimportBrowserfromaio_dt_protocolimportBrowserNamefromaio_dt_protocol.dataimportKeyEventsDEBUG_PORT:int=9222BROWSER_NAME:str=BrowserName.CHROMEPROFILE_NAME:str=BROWSER_NAME.capitalize()+"_Profile"asyncdefmain()->None:# ? Будет печатать в консоль всё, что приходит по соединению со страницей.# ? Полезно при разработке.# async def action_printer(data: dict) -> None:#     print(data)# browser, conn = await Browser.run(callback=action_printer)# ? Если на указанном порту есть запущенный браузер, происходит подключение.# ? Иначе, запуск нового браузера.browser,conn=awaitBrowser.run(debug_port=DEBUG_PORT,browser_name=BROWSER_NAME,profile_path=PROFILE_NAME)print("[- GO TO GOOGLE ... -]")awaitconn.Page.navigate("https://www.google.com",)print("[- EMULATE INPUT TEXT ... -]")input_node=awaitconn.DOM.querySelector("[type=search]")# ? Эмуляция клика в поисковую строкуawaitinput_node.click()awaitasyncio.sleep(1)# ? Вставка текстаawaitconn.Input.insertText("github PieceOfGood")awaitasyncio.sleep(1)# ? Эмуляция нажатия клавиши Enterawaitconn.extend.action.sendKeyEvent(KeyEvents.enter)awaitasyncio.sleep(1)# ? Нажатие Enter можно заменить кликом по кнопке# ? используя протокол# submit_button_selector = "div:not([jsname])>center>[type=submit]:not([jsaction])"# submit_button = await conn.DOM.querySelector(submit_button_selector)# await submit_button.click()# ? Или выполнить клик используя JavaScript# click_code = f"""\# document.querySelector("{submit_button_selector}").click();# """# await conn.extend.injectJS(click_code)print("[- WAIT FOR CLOSE PAGE ... -]")# ? Пока соединение существует, цикл выполняется.awaitconn.waitForClose()print("[- DONE -]")if__name__=='__main__':asyncio.run(main())На страницу можно легко зарегистрировать слушателей, которые будут вызываться на стороне клиентского(Python) кода. Для этого необходимо зарегистрировать вызываемую функцию в качестве такого слушателя. Это возможно выполнить двумя способами:Вручную передав методуaddBinding()доменаRuntimeимя функции в виде строки.Воспользоваться более функциональной обёрткой первого способа, выраженной в методеbindFunction()соединения.Второй способ менее многословен. Под капотом он добавляет в контекст страницы утилитуpy_call(), первым аргументом принимающую имя функции(слушателя), после чего, любое кол-во позиционных аргументов, которые ожидает эта функция, а так же позволяет прикрепить любое кол-во аргументов, передаваемых в функцию последними. Например:html="""\<html lang="ru"><head><meta charset="utf-8" /><title>Test application</title></head><body><button id="knopka">Push me</button></body><script>const btn = document.querySelector('#knopka');btn.addEventListener('click', () => {py_call("test_func", 1, "test")});</script></html>"""# ? number и text будут переданы из браузера, а bind_arg указан при регистрацииasyncdeftest_func(number:int,text:str,bind_arg:dict)->None:print(f"[- test_func -] Called with args:\n\tnumber:{number}"f"\n\ttext:{text}\n\tbing_arg:{bind_arg}")awaitconn.bindFunction(test_func,# ! слушатель{"name":"test","value":True}# ! bind_arg)# ? Если ожидается внушительный функционал прикрутить к странице, то это можно# ? сделать за один раз.# await conn.bindFunctions(#     (test_func, [ {"name": "test", "value": True} ]),#     # (any_awaitable1, [1, 2, 3])#     # (any_awaitable2, [])# )awaitconn.Page.navigate(html)HeadlessЧтобы запустить браузер в безголовом(headless) режиме, передайте пустую строку аргументу(profile_path) принимающему путь к папке профиля.importasynciofromaio_dt_protocolimportBrowser,BrowserNamefromaio_dt_protocol.utilsimportsave_img_as,async_util_callDEBUG_PORT:int=9222BROWSER_NAME:str=BrowserName.CHROMEasyncdefmain()->None:# ? Если на указанном порту есть запущенный браузер, происходит подключение.# ? Иначе, запуск нового браузера.browser,conn=awaitBrowser.run(debug_port=DEBUG_PORT,browser_name=BROWSER_NAME,profile_path="")print("[- WAITING PAGE -]")conn=awaitbrowser.waitFirstTab()print("[- GO TO GOOGLE -]")awaitconn.Page.navigate("https://www.google.com")print("[- MAKE SCREENSHOT -]")awaitasync_util_call(save_img_as,"google.png",awaitconn.extend.makeScreenshot())print("[- CLOSE BROWSER -]")awaitconn.Browser.close()print("[- DONE -]")if__name__=='__main__':asyncio.run(main())Custom serializerПоскольку обмен данными по протоколу использует формат JSON, а под капотом используется стандартная реализация, то чтобы поменять этот механизм используется глобальный объектSerializer. Например:fromaio_dt_protocolimportBrowser,Serializerfrommsgspecimportjsonasyncdefmain()->None:Serializer.decode=json.decodeSerializer.encode=lambdax:json.encode(x).decode("utf-8")browser,conn=awaitBrowser.run()...Будьте внимательны!
Метод, сериализующий данные в JSON, должен возвращать типstr, так кактолько в этом случаесообщение отправляется в текстовом фрейме, что и ожидается при обмене по протоколу.