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一堆模塊...)
希望對你有幫助...୧(﹒︠ᴗ﹒︡)୨
