CefSharp 使用 DevToolsClient 执行 Chrome DevTools Protocol(CDP) 方法,以及调试CDP

Chrome DevTools Protocol (CDP)是 Chrome 开始开放的一个WebSocket通信协议。

可以访问 https://chromedevtools.github.io/devtools-protocol/ 官方文档查看websocket通信时的各种接口调用参数。

接口通信使用json格式:

请求:

{
        "id": "消息ID",
        "method": "方法名称",
        "params": {}
}

id: messageId,数字类型,不能为0,最好不要重复,要通过messageId区分响应信息

method: CDP 方法名称(Browser.getVersion、Page.getResourceTree...)

params:  请求方法参数

响应:

{
    “id”: "请求时传入的消息Id" ,
    "result": {}
}    

开发者工具的所有操作都是遵循的这个协议,也就是说可以通过这个协议,绕开chrome 各种限制(ಥ_ಥ),比如获取网页所有标签详细信息(包括嵌套的iframe)。

执行Chrome DevTools Protocol 方法:

DevToolsClient 是CefSharp 专门对 CDP 接口做的的封装(Page、DOM、Browser...等等),或者通过ExecuteDevToolsMethodAsync手动执行方法:

声明DevToolsClient全局变量:

复制代码
//声明全局变量
DevToolsClient devTool = null;
//添加ChromiumWebBrowser初始化事件,初始化时赋值
private void Form1_Load(object sender, EventArgs e)
{
    DevToolsClient chromiumWebBrowser1.IsBrowserInitializedChanged += new EventHandler(delegate {
        devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient();
    });
}
复制代码

执行自定义方法:

复制代码
//添加一个执行方法
public async Task<string> ExecuteDevToolsMethods(string method, IDictionary<string, object> param = null)
{
       DevToolsMethodResponse resp = await devTool.ExecuteDevToolsMethodAsync(method, param);
       return resp.ResponseAsJsonString;
}
//执行
private void button1_Click(object sender, EventArgs e)
{
    ExecuteDevToolsMethods("DOM.getDocument").ContinueWith(new Action<Task<string>>((result) =>
    {
        Console.WriteLine(result.Result);
    }));
}
复制代码

注意: 不要使用Wait()等待函数,不然会导致卡死

或者使用CefSharp封装好的对象调用:

private async void button1_Click(object sender, EventArgs e)
{
    await devTool.Browser.GetVersionAsync().ContinueWith(new Action<Task<GetVersionResponse>>((resp)=> {
        Console.WriteLine(resp);
    }));
}

如果使用CefSharp封装对象调用时,需要在执行方法体上添加async/await,才不会导致程序卡死。

可以看一下 stackoverflow 中锁死的几种情况:https://stackoverflow.com/questions/65895251/cefsharp-use-devtoolsclient-execute-method-after-call-wait-function-waiting/65895577?noredirect=1#comment116512155_65895577

不熟悉async/await的原理,如果各位有好的处理建议,请留言,非常感谢!

调试CDP接口:

配置CefSettings, 指定CefSharp 启动时打开远程调试端口:

CefSettings _settings = new CefSettings();
_settings.RemoteDebuggingPort = 32999;
Cef.Initialize(_settings);
一定要在创建窗体时执行配置全局CefSettings配置:
        public Form1()
        {
            InitializeComponent();
            CefSettings _settings = new CefSettings();
            _settings.RemoteDebuggingPort = 32999;//调试端口,一会程序启动后,需要访问这个端口
            Cef.Initialize(_settings);
        }
如果在其他地方配置时会报(暂时还没找到原因...):
CEF can only be initialized once per process. This is a limitation of the underlying CEF/Chromium framework. You can change many (not all) settings at runtime through RequestContext.SetPreference.

这时启动程序,不出意外的话,直接访问: {ip}:{port}(localhost:32999):

 和chrome的远程调试页面比,要简陋一点点....

页面中列出浏览器端(CefSharp)当前打开了几个网页,打开开发者工具,切换到ws标签,在点进网页的同时,会发现建立了一个websocket长连接,每一个网页都有属于自己的一个websocket链接地址

注意: 一定要点进网页前打开开发者工具开始监听websocket,点进页面后,在打开开发者工具监听就晚了,因为websocket是长连接,在一次通信过程中,只会建立一次链接,如果在建立链接时没有打开开发者工具开启监听,ws标签下就一直不会有websocket实时通信内容..

 

 

手动调用CDP接口:

访问 {ip}:{port} (localhost:32999) / json  获取websocket链接:

 

 

 

返回结果是一个数组,数组中每个元素对应的就是当前浏览器端打开的网页信息,webSocketDebuggerUrl字段值就是链接网页是websocket调试地址.

例如,调试 百度页 , 通过websocket工具直接链接: 

 

 

Browser.getVersion: 获取浏览器端信息

 

 

 

或者通过DOM.getDocument获取百度页面标签内容(包含iframe嵌套iframe,没有同源限制,可以获取所有标签内容):

DOM.getDocument

获取网页文档层级结构.

参数:

  depth[可选]: integer (递归检索子节点深度,默认为1)

  pierce[可选]:boolean(是否遍历iframes下内容(个人理解,可能有误,请参照谷歌官网文档),默认为false)

返回:

  root: Node对象

 

 

 在执行DOM、Page.等其他模块时,最好先执行DOM.enable开启模块代理(看开发者工具在打开页时,总是先enable一堆模块...)

 

 

 希望对你有帮助...୧(﹒︠ᴗ﹒︡)୨

posted @   耿明岩  阅读(4048)  评论(2编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!
希望能帮助到你,顺利解决问题! ...G(^_−)☆
点击右上角即可分享
微信分享提示