我正在尝试使用 CDP 运行测试,
webdriver.execute_cdp_cmd('Network.enable', {})
使用远程网络驱动程序(在 Selenoid 中)。但出现此错误:
AttributeError:“WebDriver”对象没有属性“execute_cdp_cmd”。 在本地环境下效果很好。我尝试过使用 Selenium 3.141.0 和 4.1.3。
我熟悉 PyCDP 文档(https://py-cdp.readthedocs.io/en/latest/getting_started.html),但我不知道如何正确使用它。
为什么它不能与远程网络驱动程序一起使用?有人有在 Selenium 4 中使用 python 执行 CDP 命令的示例吗?
我使用以下功能:
capability = { 'loggingPrefs': {'browser': 'ALL'}, 'goog:loggingPrefs': {'performance': 'ALL'}, "browserName": "chrome", "browserVersion": "99.0", “selenoid:options”: { “enableVNC”: True, “enableVideo”: False } }
如果 request.config.getoption('--remote'): 驱动程序= webdriver.Remote(command_executor ='selenoid.dev:4444 / wd / hub',desired_capability =功能,选项=选项)
看起来远程 Web 驱动程序不支持 CDP。
找到了这个问题的甜蜜解决方案:
import json
def send(driver, cmd, params={}):
resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
url = driver.command_executor._url + resource
body = json.dumps({'cmd': cmd, 'params': params})
response = driver.command_executor._request('POST', url, body)
return response.get('value')
send(webdriver, 'Network.enable', {})
2024 年,CDP 似乎正在为远程驱动程序工作。
我的代码示例以及我如何使用它来获取特定的第 3 方 API 调用响应的解决方案:
import json
import logging
import sys
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.remote.webdriver import WebDriver
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logger = logging.getLogger(__name__)
def prepare_browser() -> WebDriver:
chrome_options = Options()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_experimental_option(
"prefs",
{
"intl.accept_languages": "en,en_US",
"profile.managed_default_content_settings.images": 2,
},
)
chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"})
chrome_options.set_capability("browserVersion", "latest")
chrome_options.set_capability(
"selenoid:options", {"enableVNC": True, "enableVideo": False}
)
return webdriver.Remote(
command_executor="http://<selenoid-host>:4444/wd/hub",
options=chrome_options,
)
def get_browser_request_body(driver: WebDriver, request_id: str) -> str:
browser_response = driver.execute(
driver_command="executeCdpCommand",
params={
"cmd": "Network.getResponseBody",
"params": {"requestId": request_id},
},
)
return browser_response["value"]["body"].split("\n")[-1]
def get_browser_performance_logs(driver: WebDriver) -> list[dict]:
browser_response = driver.execute(
driver_command="getLog", params={"type": "performance"}
)
return browser_response["value"]
def intercept_json_by_url_part(driver: WebDriver, url_part: str) -> str | None:
performance_logs = get_browser_performance_logs(driver=driver)
for log in performance_logs:
message = log["message"]
if "Network.response" not in log["message"]:
continue
params = json.loads(message)["message"].get("params")
response = params.get("response") if params else None
if response and url_part in response["url"]:
logger.info(f"Found required url part in url: {response['url']}")
return get_browser_request_body(
driver=driver, request_id=params["requestId"]
)
def main() -> None:
driver = prepare_browser()
driver.get("https://demo.realworld.io")
response = intercept_json_by_url_part(driver=driver, url_part="/api/tags")
print(response)
"""
Response:
{"tags":["est","enim","ipsum","repellat","exercitationem","eos","quia","tenetur","facilis","consequatur"]}
"""
if __name__ == "__main__":
main()
重要提示(2024-03-28):
我上面的代码示例将从 Selenium 4.16.0 开始工作
出于任何原因,如果您想通过 Chrome for Selenium 4.0.0 使用远程连接 < selenium < 4.16.0 you should configure the Remote Connection class for Chrome:
from selenium.webdriver import DesiredCapabilities
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
class ChromeRemoteConnection(ChromiumRemoteConnection):
browser_name = DesiredCapabilities.CHROME["browserName"]
def __init__(
self,
remote_server_addr: str,
keep_alive: bool = True,
ignore_proxy: bool = False,
) -> None:
super().__init__(
remote_server_addr=remote_server_addr,
vendor_prefix="goog",
browser_name=ChromeRemoteConnection.browser_name,
keep_alive=keep_alive,
ignore_proxy=ignore_proxy,
)
def prepare_browser() -> WebDriver:
chrome_options = Options()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_experimental_option(
"prefs",
{
"intl.accept_languages": "en,en_US",
"profile.managed_default_content_settings.images": 2,
},
)
chrome_options.set_capability("goog:loggingPrefs", {"performance": "ALL"})
chrome_options.set_capability("browserVersion", "latest")
chrome_options.set_capability("browserName", "chrome")
chrome_options.set_capability(
"selenoid:options", {"enableVNC": True, "enableVideo": False}
)
command_executor = ChromeRemoteConnection(
remote_server_addr="http://<selenoid-host>:4444/wd/hub"
)
return webdriver.Remote(
command_executor=command_executor, options=chrome_options
)
在Python的Selenium库中使用CDP的官方方法是使用双向功能
它是一个异步 API。但是,您可以使用 trio 将其转换为同步 API。我使用包装函数来使其更易于使用。
from selenium import webdriver
# You may need to change this if you need to use different version of CDP
import selenium.webdriver.common.devtools.v111 as devtools
import trio
def execute_cdp(driver: webdriver.Remote, cmd):
async def execute_cdp_async():
async with driver.bidi_connection() as session:
cdp_session = session.session
return await cdp_session.execute(cmd)
# It will have error if we use asyncio.run
# https://github.com/SeleniumHQ/selenium/issues/11244
return trio.run(execute_cdp_async)
# Use it this way:
execute_cdp(driver, devtools.network.enable())
mhtml = execute_cdp(driver, devtools.page.capture_snapshot())