Chrome 的 F12 想必你們已經再熟悉不過了,若是你是一名前端er,那玩意簡直就像我們的飯碗同樣。。html
這是 Chrome 自帶的調試工具,日常 debug 頁面全靠它,然而它倒是經過一個叫 Chrome DevTools Protocol 的協議來和目標頁面通信的。前端
chrome v 60 + 能夠用下面參數暴露出協議。java
chrome.exe --remote-debugging-port=9222
Chrome DevTools Protocol 是基於 WebScoket 協議的,當使用上面代碼啓動 chrome 後,咱們能夠在另外一個頁面輸入 localhost:9222 來查看全部能夠被 inspect 的頁面。node
上面的 google 和 tmall 正對應着當前打開的 2 個頁籤。git
點擊任意一個,進入調試頁面。github
能夠看出 chrome 的調試器本質也是一個 web 頁面而已。這是咱們打開調試器的調試器。web
這是一個奇妙的場景,上下 2 個調試器,上面那個是用來調試目標頁面的,下面那個是調試器的調試器。點擊下面的調試器,切換到 NetWork 頁籤, F5 刷新一下,會發現一個 WebSocket 鏈接。chrome
這個就是 devTool 用於鏈接調試頁面的鏈接了,這個 websocket 鏈接遵循 Chrome DevTools Protocol 協議
看一下它們之間到底發送了什麼東東。。
{"id":1,"method":"Network.enable","params":{"maxTotalBufferSize":10000000,"maxResourceBufferSize":5000000}} {"id":2,"method":"Page.enable"} ... ... {"id":36,"method":"Overlay.highlightNode","params":{"highlightConfig":{"showInfo":true,"showRulers":false,"showExtensionLines":false,"contentColor":{"r":111,"g":168,"b":220,"a":0.66},"paddingColor":{"r":147,"g":196,"b":125,"a":0.55},"borderColor":{"r":255,"g":229,"b":153,"a":0.66},"marginColor":{"r":246,"g":178,"b":107,"a":0.66},"eventTargetColor":{"r":255,"g":196,"b":196,"a":0.66},"shapeColor":{"r":96,"g":82,"b":177,"a":0.8},"shapeMarginColor":{"r":96,"g":82,"b":127,"a":0.6},"displayAsMaterial":true,"cssGridColor":{"r":75,"g":0,"b":130}},"nodeId":4}}
能夠看出每條信息的格式是有一個遞增的 id 值,而後有 method 和 params 參數。這些消息指揮者被調試頁面作出各類各樣的動做,好比 hover 到 node 上讓該節點高亮要使用 "Overlay.highlightNode"。其餘具體的命令用法能夠去官網查詢。
也就是說,任何一個實現了 Chrome DevTools Protocol 的程序均可以用來調試頁面,chrome 這個協議等因而開放了用程序控制頁面動做的接口。
下面上一個小例:
const initlize = [ {"id":1,"method":"Network.enable","params":{"maxTotalBufferSize":10000000,"maxResourceBufferSize":5000000}}, {"id":3,"method":"Page.getResourceTree"}, {"id":4,"method":"Profiler.enable"}, {"id":5,"method":"Runtime.enable"}, {"id":6,"method":"Debugger.enable"}, {"id":7,"method":"Debugger.setPauseOnExceptions","params":{"state":"none"}}, {"id":8,"method":"Debugger.setAsyncCallStackDepth","params":{"maxDepth":32}}, {"id":9,"method":"DOM.enable"} , {"id":10,"method":"CSS.enable"} , {"id":11,"method":"Overlay.enable"}, {"id":12,"method":"Overlay.setShowViewportSizeOnResize","params":{"show":true}}, {"id":13,"method":"Log.enable"}, {"id":14,"method":"Log.startViolationsReport","params":{"config":[{"name":"longTask","threshold":200},{"name":"longLayout","threshold":30},{"name":"blockedEvent","threshold":100},{"name":"blockedParser","threshold":-1},{"name":"handler","threshold":150},{"name":"recurringHandler","threshold":50},{"name":"discouragedAPIUse","threshold":-1}]}}, {"id":15,"method":"ServiceWorker.enable"}, {"id":16,"method":"Inspector.enable"}, {"id":17,"method":"Target.setAutoAttach","params":{"autoAttach":true,"waitForDebuggerOnStart":true}}, {"id":18,"method":"Target.setAttachToFrames","params":{"value":true}}, {"id":19,"method":"Target.setDiscoverTargets","params":{"discover":true}}, {"id":20,"method":"Target.setRemoteLocations","params":{"locations":[{"host":"localhost","port":9229}]}}, {"id":21,"method":"Page.setAutoAttachToCreatedPages","params":{"autoAttach":false}} ]; var socket = new WebSocket('ws://localhost:9222/devtools/page/70025d54-d942-446d-9bf9-ffdd93b7896d'); socket.onmessage = function (e, a) { console.log(e, a) } socket.onopen = function () { for (let i in initlize) { socket.send(JSON.stringify(initlize[i])) } } socket.send(`{"id":22,"method":"Runtime.compileScript","params":{"expression":"alert(1)","sourceURL":"","persistScript":false,"executionContextId":1}}`); socket.send(`{"id":23,"method":"Runtime.evaluate","params":{"expression":"alert(1)","objectGroup":"console","includeCommandLineAPI":true,"silent":false,"contextId":1,"returnByValue":false,"generatePreview":true,"userGesture":true,"awaitPromise":false}}`);
上面咱們模擬了發送 alert(1) 到目標頁面的過程。
用 puppeteer 控制 headless chrome
直接操做協議有點痛苦, chrome-remote-interface 封裝了協議的大部分功能,但仍是須要閱讀協議內容,去看,去查。這仍是有些痛苦啊。。。 puppeteer 提供了比較高級的 API,使用很是簡單,推薦一波。
const puppeteer = require('puppeteer'); (async() => { const browser = await puppeteer.launch({ headless: true }); // default is true const page = await browser.newPage(); await page.setRequestInterceptionEnabled(true); page.on('request', interceptedRequest => { console.log(interceptedRequest); // if (interceptedRequest.url.endsWith('.png') || interceptedRequest.url.endsWith('.jpg')) // interceptedRequest.abort(); // else // interceptedRequest.continue(); }); await page.setViewport({ width: 1024, height: 768 }); await page.goto('https://www.jd.com', { waitUntil: 'networkidle' }); const result = await page.evaluate(() => { return Promise.resolve(document.title); }); console.log(result); await page.screenshot({ path: 'screenshot.png' }); browser.close(); })();
感受使用它能夠徹底替代 phantomjs 等腳本瀏覽器了。google 大廠官方出品,可靠性絕對靠譜。
調試 nodejs
node 也支持 Chrome DevTools Protocol ,在 node 7+ 的版本,輸入 node --inspect-brk app.js 便可開啓調試。
node --inspect-brk app.js
而後打開 chrome 在地址欄輸入: chrome://inspect 在下方 target 中選取須要調試的 node 進程。