Chrome DevTools Protocol (CDP)是 Chrome 開(kāi)始開(kāi)放的一個(gè)WebSocket通信協(xié)議。 可以訪問(wèn) https://chromedevtools./devtools-protocol/ 官方文檔查看websocket通信時(shí)的各種接口調(diào)用參數(shù)。 接口通信使用json格式: 請(qǐng)求: { "id": "消息ID", "method": "方法名稱(chēng)", "params": {} } id: messageId,數(shù)字類(lèi)型,不能為0,最好不要重復(fù),要通過(guò)messageId區(qū)分響應(yīng)信息 method: CDP 方法名稱(chēng)(Browser.getVersion、Page.getResourceTree...) params: 請(qǐng)求方法參數(shù) 響應(yīng): { “id”: "請(qǐng)求時(shí)傳入的消息Id" , "result": {} } 開(kāi)發(fā)者工具的所有操作都是遵循的這個(gè)協(xié)議,也就是說(shuō)可以通過(guò)這個(gè)協(xié)議,繞開(kāi)chrome 各種限制(?_?),比如獲取網(wǎng)頁(yè)所有標(biāo)簽詳細(xì)信息(包括嵌套的iframe)。 執(zhí)行Chrome DevTools Protocol 方法:DevToolsClient 是CefSharp 專(zhuān)門(mén)對(duì) CDP 接口做的的封裝(Page、DOM、Browser...等等),或者通過(guò)ExecuteDevToolsMethodAsync手動(dòng)執(zhí)行方法: 聲明DevToolsClient全局變量://聲明全局變量 DevToolsClient devTool = null; //添加ChromiumWebBrowser初始化事件,初始化時(shí)賦值 private void Form1_Load(object sender, EventArgs e) { DevToolsClient chromiumWebBrowser1.IsBrowserInitializedChanged += new EventHandler(delegate { devTool = chromiumWebBrowser1.GetBrowser().GetDevToolsClient(); }); } 執(zhí)行自定義方法://添加一個(gè)執(zhí)行方法 public async Task<string> ExecuteDevToolsMethods(string method, IDictionary<string, object> param = null) { DevToolsMethodResponse resp = await devTool.ExecuteDevToolsMethodAsync(method, param); return resp.ResponseAsJsonString; } //執(zhí)行 private void button1_Click(object sender, EventArgs e) { ExecuteDevToolsMethods("DOM.getDocument").ContinueWith(new Action<Task<string>>((result) => { Console.WriteLine(result.Result); })); } 注意: 不要使用Wait()等待函數(shù),不然會(huì)導(dǎo)致卡死 或者使用CefSharp封裝好的對(duì)象調(diào)用:private async void button1_Click(object sender, EventArgs e) { await devTool.Browser.GetVersionAsync().ContinueWith(new Action<Task<GetVersionResponse>>((resp)=> { Console.WriteLine(resp); })); } 如果使用CefSharp封裝對(duì)象調(diào)用時(shí),需要在執(zhí)行方法體上添加async/await,才不會(huì)導(dǎo)致程序卡死。 可以看一下 stackoverflow 中鎖死的幾種情況:https:///questions/65895251/cefsharp-use-devtoolsclient-execute-method-after-call-wait-function-waiting/65895577?noredirect=1#comment116512155_65895577 不熟悉async/await的原理,如果各位有好的處理建議,請(qǐng)留言,非常感謝! 調(diào)試CDP接口:配置CefSettings, 指定CefSharp 啟動(dòng)時(shí)打開(kāi)遠(yuǎn)程調(diào)試端口: CefSettings _settings = new CefSettings(); _settings.RemoteDebuggingPort = 32999; Cef.Initialize(_settings); 一定要在創(chuàng)建窗體時(shí)執(zhí)行配置全局CefSettings配置: public Form1() { InitializeComponent(); CefSettings _settings = new CefSettings(); _settings.RemoteDebuggingPort = 32999;//調(diào)試端口,一會(huì)程序啟動(dòng)后,需要訪問(wèn)這個(gè)端口 Cef.Initialize(_settings); } 如果在其他地方配置時(shí)會(huì)報(bào)(暫時(shí)還沒(méi)找到原因...): 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.
這時(shí)啟動(dòng)程序,不出意外的話,直接訪問(wèn): {ip}:{port}(localhost:32999): 和chrome的遠(yuǎn)程調(diào)試頁(yè)面比,要簡(jiǎn)陋一點(diǎn)點(diǎn).... 頁(yè)面中列出瀏覽器端(CefSharp)當(dāng)前打開(kāi)了幾個(gè)網(wǎng)頁(yè),打開(kāi)開(kāi)發(fā)者工具,切換到ws標(biāo)簽,在點(diǎn)進(jìn)網(wǎng)頁(yè)的同時(shí),會(huì)發(fā)現(xiàn)建立了一個(gè)websocket長(zhǎng)連接,每一個(gè)網(wǎng)頁(yè)都有屬于自己的一個(gè)websocket鏈接地址。 注意: 一定要點(diǎn)進(jìn)網(wǎng)頁(yè)前打開(kāi)開(kāi)發(fā)者工具開(kāi)始監(jiān)聽(tīng)websocket,點(diǎn)進(jìn)頁(yè)面后,在打開(kāi)開(kāi)發(fā)者工具監(jiān)聽(tīng)就晚了,因?yàn)閣ebsocket是長(zhǎng)連接,在一次通信過(guò)程中,只會(huì)建立一次鏈接,如果在建立鏈接時(shí)沒(méi)有打開(kāi)開(kāi)發(fā)者工具開(kāi)啟監(jiān)聽(tīng),ws標(biāo)簽下就一直不會(huì)有websocket實(shí)時(shí)通信內(nèi)容..
手動(dòng)調(diào)用CDP接口:訪問(wèn) {ip}:{port} (localhost:32999) / json 獲取websocket鏈接:
返回結(jié)果是一個(gè)數(shù)組,數(shù)組中每個(gè)元素對(duì)應(yīng)的就是當(dāng)前瀏覽器端打開(kāi)的網(wǎng)頁(yè)信息,webSocketDebuggerUrl字段值就是鏈接網(wǎng)頁(yè)是websocket調(diào)試地址. 例如,調(diào)試 百度頁(yè) , 通過(guò)websocket工具直接鏈接:
Browser.getVersion: 獲取瀏覽器端信息
或者通過(guò)DOM.getDocument獲取百度頁(yè)面標(biāo)簽內(nèi)容(包含iframe嵌套iframe,沒(méi)有同源限制,可以獲取所有標(biāo)簽內(nèi)容): DOM.getDocument 獲取網(wǎng)頁(yè)文檔層級(jí)結(jié)構(gòu). 參數(shù): depth[可選]: integer (遞歸檢索子節(jié)點(diǎn)深度,默認(rèn)為1) pierce[可選]:boolean(是否遍歷iframes下內(nèi)容(個(gè)人理解,可能有誤,請(qǐng)參照谷歌官網(wǎng)文檔),默認(rèn)為false) 返回: root: Node對(duì)象
在執(zhí)行DOM、Page.等其他模塊時(shí),最好先執(zhí)行DOM.enable開(kāi)啟模塊代理(看開(kāi)發(fā)者工具在打開(kāi)頁(yè)時(shí),總是先enable一堆模塊...)
希望對(duì)你有幫助...?(﹒??﹒?)? |
|
來(lái)自: 創(chuàng)始元靈6666 > 《work》