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一堆模塊...)

 

 

 希望對你有幫助...୧(﹒︠ᴗ﹒︡)୨


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM