前言

寒假在实验室的工作(摸鱼)中需要使用 Chrome 的远程调试协议(Remote debugging protocol),所以对这个协议学习了一番,现在把学到的简单做下整理~

关于 Remote-Debugging-Protocol

Chrome DevTools Protocol 允许工具对 Chromium,Chrome 和其它基于 Blink 的浏览器进行测试、检查、调试以及配置。现在有许多项目基于该协议,比如说 Chrome DevTools,同时该协议也由 Chrome DevTools 项目的开发团队负责维护。

Chrome DevTools Protocol 被分为多个域(DOM,Debugger,Network 等),每个域定义了相应支持的 commands 和相关的 events,commands 和 events 都是固定结构的序列化 JSON 对象。协议使用 Websocket 来与页面建立通信,相应数据由发送给页面的 commands 和页面自身产生的 events 共同组成。

Chrome 开发者工具是远程调试协议的主要使用者,当然,第三方开发者也允许调用这个协议来与页面进行交互调试。

ps: Chrome DevTools Protocol、Remote Debugging Protocol、Chrome Debugging Protocol 其实指的是同一个协议,不知道为什么名字什么的变得这么多了???这里用官网的描述所以称为 Chrome DevTools Protocol。

启动

如果想使用 Chrome Remote Debugging Protocol,那么 Chrome/Chromium 需要在运行在一个已知的的端口上(默认是 9222),相应开启命令如下:

# windows
chrome.exe --remote-debugging-port=9222
# linux
google-chrome --remote-debugging-port=9222

使用开发者工具(远程协议版)

开启后访问 http://localhost:9222/ 即可看到当前浏览器打开的所有页面(包括正在运行的 chrome 插件)

![视察页面](/images/posts/Chrome 远程调试协议不完全指南/inspectable-pages.png)

点击当前页面上的 Github 标签页选项,可以看到页面跳转到了 Github 页面所对应开发者工具页面

![视察 Github](/images/posts/Chrome 远程调试协议不完全指南/inspect-github.png)

可以发现该页面和直接在 Github 页面按 F12 打开开发者工具所弹出的开发者工具页面类似

![开发者工具页面](/images/posts/Chrome 远程调试协议不完全指南/github.png)

官方解释原因如下:

  • When you navigate your client browser to the remote’s Chrome port, Developer Tools front-end is being served from the host Chrome as a Web Application from the Web Server.
  • It fetches HTML, JavaScript and CSS files over HTTP
  • Once loaded, Developer Tools establishes a Web Socket connection to its host and starts exchanging JSON messages with it

大意上就是说,Chrome 本身作为一个服务器向你提供相应的数据,而你打开的这个页面则作为前端页面对相应数据进行展示。二者之间通过 Websocket 建立了连接并通过 JSON 交互数据。所以我们就能像使用内嵌的开发者工具一样使用当前页面上远程调试协议版的开发者工具了。

功能介绍

远程调试协议运行我们通过 http://localhost:9222/json/ 一系列的 URL 对应的页面作为客户端,对 Chrome 进行远程调试操作。

支持的操作如下:

  • list 列举所以标签页
  • new 新标签页
  • close 关闭标签页
  • activate 激活标签页
  • version 显示版本

默认访问 http://localhost:9222/json/ 等价于访问 http://localhost:9222/json/list

list

访问 http://localhost:9222/json/list,即可看到当前所有页面的描述信息,以 JSON 对象的数组形式给出

[ {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/(AF98D91E5E0F7F39B954D6C74D91D478)",
   "id": "(AF98D91E5E0F7F39B954D6C74D91D478)",
   "title": "localhost:9222/json/list",
   "type": "page",
   "url": "http://localhost:9222/json/list",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/(AF98D91E5E0F7F39B954D6C74D91D478)"
}, {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/(B12271C6B090237B0A3A24E619F4443)",
   "faviconUrl": "https://www.baidu.com/favicon.ico",
   "id": "(B12271C6B090237B0A3A24E619F4443)",
   "title": "百度一下,你就知道",
   "type": "page",
   "url": "https://www.baidu.com/",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/(B12271C6B090237B0A3A24E619F4443)"
},
 {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/(47EB7F10ED42189056F8BC191EB1D7E5)",
   "faviconUrl": "https://assets-cdn.github.com/favicon.ico",
   "id": "(47EB7F10ED42189056F8BC191EB1D7E5)",
   "title": "GitHub",
   "type": "page",
   "url": "https://github.com/",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/(47EB7F10ED42189056F8BC191EB1D7E5)"
}]

具体 JSON 格式如下:

{
   "description": 页面描述,基本都为空,
   "devtoolsFrontendUrl": 相对应开发者工具页面的 URL,
   "faviconUrl": 图标 URL,
   "id": 页面 id,每个页面唯一,
   "title": 页面标题,
   "type": 页面类型,最常见的是 page,
   "url": 页面 URL,
   "webSocketDebuggerUrl": websocket 对应的 URL
}

new

访问 http://localhost:9222/json/new,浏览器会创建一个新的 tab 页并将当前页面的信息以 JSON 格式给出

![new tab](/images/posts/Chrome 远程调试协议不完全指南/new.png)

如果在 new 后面添加相应 url 的话,会打开指定 url 的页面,如 http://localhost:9222/json/new?https://github.com/

![new tab with url](/images/posts/Chrome 远程调试协议不完全指南/new-url.png)

close

我们可以通过访问 http://localhost:9222/json/close/(xxxx_id) 的方式,关闭相应 id 所对应的 tab

比如说 我们刚刚打开了 Github 的一个页面,该 tab 对应的 id 是 (30217EC62207250FA2096141D1A2A726),那么我们可以通过访问 http://localhost:9222/json/close/(30217EC62207250FA2096141D1A2A726) 来关闭该页面

![close](/images/posts/Chrome 远程调试协议不完全指南/close.png)

activate

启用某个已打开的标签页,即让浏览器显示指定的标签页。使用方法类似 close,我们可以通过访问 http://localhost:9222/json/activate/(xxxx_id) 的方式来重新启用该页面。

比如我们想激活一个 id 为 (75139F5AF5088D06DB59F1BCD10BC3F9) 的百度页面,那么我们需要访问 http://localhost:9222/json/activate/(75139F5AF5088D06DB59F1BCD10BC3F9) ,此时百度页面会被重新激活,Chrome 会将其切换成显示状态。

![启用百度](/images/posts/Chrome 远程调试协议不完全指南/activate-baidu.png)

重新回到之前访问的 URL,我们会看到页面上显示的 Target activated

![结果](/images/posts/Chrome 远程调试协议不完全指南/activate-result.png)

version

返回相应浏览器版本

![版本](/images/posts/Chrome 远程调试协议不完全指南/version.png)

使用 nodejs 封装的 remote debugging protocol

nodejs 已经有大牛将远程调试协议封装好了,我们只需要用(膜)就好了

项目参见 chrome-remote-interface

安装

如果在相应项目中需要使用 chrome-remote-interface,使用 npm 进行安装即可

# local
npm install chrome-remote-interface
# global
npm install -g chrome-remote-interface

使用

chrome-remote-interface 有两种使用模式,一种就是像正常包一样在项目中导入并使用,比如:

const CDP = require('chrome-remote-interface');

(async ()=> {
    try {
        // connect to endpoint
        var client = await CDP();
        // extract domains
        const {Network, Page} = client;
        // setup handlers
        Network.requestWillBeSent((params) => {
            console.log(params.request.url);
        });
        // enable events then start!
        await Promise.all([Network.enable(), Page.enable()]);
        await Page.navigate({url: 'https://github.com'});
        await Page.loadEventFired();
    } catch (err) {
        console.error(err);
    } finally {
        if (client) {
            await client.close();
        }
    }
})();

还有一种是通过全局方式安装并使用其内嵌的命令行客户端,类似 shell 的交互模式,具体命令和协议支持的命令保持一致,如

ubuntu@ubuntu-pc:~$ chrome-remote-interface list
[
    {
        "description": "",
        "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/(C847D92465ACD4B12F5C697754CB78A0)",
        "faviconUrl": "https://www.baidu.com/favicon.ico",
        "id": "(C847D92465ACD4B12F5C697754CB78A0)",
        "title": "百度一下,你就知道",
        "type": "page",
        "url": "https://www.baidu.com/",
        "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/(C847D92465ACD4B12F5C697754CB78A0)"
    }
]
ubuntu@ubuntu-pc:~$ chrome-remote-interface new https://github.com
{
    "description": "",
    "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/(4525E3F126B79F25D53A5BB7DD8A92FC)",
    "id": "(4525E3F126B79F25D53A5BB7DD8A92FC)",
    "title": "",
    "type": "page",
    "url": "https://github.com/",
    "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/(4525E3F126B79F25D53A5BB7DD8A92FC)"
}
chrome-remote-interface close "(4525E3F126B79F25D53A5BB7DD8A92FC)"
ubuntu@ubuntu-pc:~$ chrome-remote-interface version
{
    "Browser": "Chrome/64.0.3282.119",
    "Protocol-Version": "1.2",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36",
    "V8-Version": "6.4.388.40",
    "WebKit-Version": "537.36 (@5e944b2d79bce5f96dc35a05a76b03d65a6e9a0a)",
    "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/ddff5441-7de0-4d70-82f6-9b0db5ead44d"
}

使用 inspect 命令,chrome-remote-interface 会以 REPL 方式执行命令操作和事件绑定:

ubuntu@ubuntu-pc:~$ chrome-remote-interface inspect
>>> Page.enable()
...
{}
>>> Page.navigate({url:'https://github.com'})
...
{ frameId: '(C847D92465ACD4B12F5C697754CB78A0)',
  loaderId: '(8FB578D2113991D14157A10E7D3FB7D1)' }
>>> Runtime.evaluate({expression: 'window.location.toString()'})
...
{ result: { type: 'string', value: 'https://github.com/' } }
>>>

由于 chrome-remote-interface 支持的命令和事件和 Chrome 远程调试协议保持一致,所以需要参考 API 文档可以直接访问 官方 API文档

不过感觉类似 shell 模式的作用偏向于短时间的调试,要写自动化脚本什么的还是需要使用 nodejs 项目导入包的方式一行一行写代码,偷懒是不可能的(笑

以上就是关于 Chrome 远程调试协议的介绍,希望能在年前再把对网页自动化性能分析的学习总结一下(正经脸

参考链接