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一堆模块...)
希望对你有帮助...୧(﹒︠ᴗ﹒︡)୨
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!