cdp協議簡介
啥是cdp
根據官網的說法,cdp(Chrome DevTools Protocol) 允許我們檢測,除錯Chromium, Chrome 和其他基於 Blink的 瀏覽器. 這個協議被廣泛使用. 其中最著名的是 Chrome DevTools,協議的api也由這個團隊維護。
使用cdp的姿勢
首先需要開啟: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
如果在瀏覽器中,當你開啟devtools時,其實你已經在使用cdp了,只是感知不深罷了,一種辦法可以更直觀的感知cdp,就是開啟devtools的devtools,具體操作如下:
- 將開發者工具設定為獨立視窗,dock side點第一個
- 在開發者工具上再使用快捷鍵
ctrl+shift+i
,就可以開啟開發者工具的開發者工具了(就是先開啟開發者工具成獨立視窗;再在這個獨立視窗上再用快捷鍵ctrl+shift+i,又打開了開發者工具
),現在在新開啟的開發者工具的console裡面,輸入下面的程式碼:
let Main = await import('./main/main.js');
Main.MainImpl.sendOverProtocol('Runtime.evaluate', {expression: 'alert (12345)'});
這時網頁會alert 12345,你會發現平時在控制檯簡單的程式碼執行,其實是通過cdp遠端呼叫網頁的js引擎去執行再返回結果的。
除此之外,protocol monitor也可以幫助我們更直觀的理解cdp。
幾個重要的URL
當一個頁面暴露出它的remote debugging port時,我們就可以藉助cdp來對這個網頁進行remote debugging了。由於cdp是藉助websocket實現的,所以,在一切開始之前,有兩個url是比較重要的
http://localhost:[port]/json/list
http://localhost:[port]/json/version
這兩個url,可以讓我們拿到網頁的websocket url,json/list返回的資料類似於:
[ { description: "", devtoolsFrontendUrl: "/devtools/inspector.html?ws=localhost:8080/devtools/page/a31c4d5c-b0df-48e8-8dcc-7c98964e2ebe", id: "a31c4d5c-b0df-48e8-8dcc-7c98964e2ebe", title: "", type: "page", url: "xxx://xxx", webSocketDebuggerUrl: "ws://localhost:8080/devtools/page/a31c4d5c-b0df-48e8-8dcc-7c98964e2ebe" } ]
其中webSocketDebuggerUrl就是我們需要的開啟remote debugging 的鑰匙
重頭戲websocket
接下來我們連上ws,就可以愉快的遠端操作頁面了,正如chrome devtools所做的那樣,下面是一個例子:
const WebSocket = require('ws');
const puppeteer = require('puppeteer');
(async () => {
// Puppeteer launches browser with a --remote-debugging-port=0 flag,
// parses Remote Debugging URL from Chromium's STDOUT and exposes
// it as |browser.wsEndpoint()|.
const browser = await puppeteer.launch();
// Create a websocket to issue CDP commands.
const ws = new WebSocket(browser.wsEndpoint(), {perMessageDeflate: false});
await new Promise(resolve => ws.once('open', resolve));
console.log('connected!');
ws.on('message', msg => console.log(msg));
console.log('Sending Target.setDiscoverTargets');
ws.send(JSON.stringify({
id: 1,
method: 'Target.setDiscoverTargets',
params: {
discover: true
},
}));
})();
更多例子可以在這裡
jsonRPC
如上面例子所示,當ws連線後,一個發給瀏覽器的指令大概包括3部分id,method,params,比如一個執行一段console.log('hello')程式碼的指令:
{
"id": 235,
"method": "Runtime.evaluate",
"params": {
"expression": "console.log('hello');",
"objectGroup": "console",
"includeCommandLineAPI": true,
"silent": false,
"contextId": 1,
"returnByValue": false,
"generatePreview": true,
"userGesture": true,
"awaitPromise": false
}
}
chrome devtools可以完成的功能非常龐大,而這些功能基本都是使用這樣的一個個指令實現的,讓人想起那句古老的中國名言:九層之臺,起於壘土。本文完
參考資料:
https://chromedevtools.github.io/devtools-protocol
https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md
文章來源: www.cnblogs.com,作者:nobody-junior,版權歸原作者所有,如需轉載,請聯絡作者。
原文連結:https://www.cnblogs.com/imgss/p/12852595.html
這邊我選擇的是 python 的 pychromegithub 地址,使用方法很簡單,直接看 github 上它的 Demo
這個庫依賴websocket-client
獲取 performance api 資料
這裡使用 Runtime Domain 中執行 JavaScript 指令碼的 APIRuntime.evaluate
# 開始前先啟動chrome,啟動chrome必須帶上引數`--remote-debugging-port=9222`開啟遠端除錯否則無法與chrome互動
browser = pychrome.Browser('http://127.0.0.1:%d' % 9222)
tab = browser.new_tab()
tab.start()
tab.Runtime.enable()
tab.Page.navigate(url={你的頁面地址})
# 設定等待頁面載入完成的時間
tab.wait(10)
# 執行js指令碼
timing_remote_object = tab.Runtime.evaluate(
expression='performance.timing'
)
# 獲取performance.timing結果資料
timing_properties = tab.Runtime.getProperties(
objectId=timing_remote_object.get('result').get('objectId')
)
timing = {}
for item in timing_properties.get('result'):
if item.get('value', {}).get('type') == 'number':
timing[item.get('name')] = item.get('value').get('value')
# 獲取performance.getEntries()資料
entries_remote_object = tab.Runtime.evaluate(
expression='performance.getEntries()'
)
entries_properties = tab.Runtime.getProperties(
objectId=entries_remote_object.get('result').get('objectId')
)
entries_values = []
for item in entries_properties.get('result'):
if item.get('name').isdigit():
url_timing_properties = tab.Runtime.getProperties(
objectId=item.get('value').get('objectId')
)
entries_value = {}
for son_item in url_timing_properties.get('result'):
if (son_item.get('value', {}).get('type') == 'number'or
son_item.get('value', {}).get('type') == 'string'):
entries_value