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