Headless Chrome 踩坑之旅

2,976 阅读3分钟
原文链接: zhuanlan.zhihu.com
4月13号,PhantomJS 的主要维护者突然宣布不干了,原因是 Google 做了一个可以在服务端跑的 Chrome 模式。

Google 在 chrome 59 的版本中添加了一个 Headless 模式,可以在这里 查看原文。大致的功能就是能在服务器跑一个 Chrome 浏览器,具有 Chrome 的绝大部分功能。Amazing!!!这下子都不用模拟浏览器环境了,直接就整了一个浏览器的环境,值得折腾一下。

然后,我就落入了一个深基坑。。。。废话少说,直接告诉大家如何开玩。

1. 准备 Chrome


首先,你需要一个支持 Headless 模式的 Chrome。
Linux 平台请安装最新的 dev 版本,macOS 平台请安装最新的 金丝雀 版本,Win 平台,我还没试过,知道的朋友可以分享一下。

2. 运行 Headless Chrome


安装好之后,运行如下命令:
Linux:
google-chrome-unstable --headless --remote-debugging-port=9222 --disable-gpu https://shimo.im
macOS:
/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --headless --remote-debugging-port=9222 https://shimo.im/
--headless 就是开启 headless 模式。
--remote-debugging-port=9222 这是开启 Chrome 的端口是 9222,这样子 Chrome 就类似于微服务一样啦。
--disable-gpu 不使用 gpu,既然是在后端运行的 Chrome 当然不需要 gpu 啦。
最后的网址随意填写,我在这写的是我们的产品——石墨文档
随意打开一个浏览器,输入 http://localhost:9222 可以得到如下的效果。

看到这个就证明,你成功开启了 Headless Chrome 的服务。

3. 调用 Chrome


调用 Chrome 需要用到一个 node 模块——chrome-remote-interface
官网的例子足够本地测试使用,我们团队在实际使用过程中发现了不少坑。

第一个坑是远程连接,CDP 中提供了 remote 的配置来允许你远程连接 Chrome,笔者第一次使用时,就遇上了一个 bug,Headless Chrome 还没有完美对接,我刚想着给作者提一个 bug。

作者立马修了。。。。。为他点赞,但是在实际使用过程中,还是需要设置好 nginx 的反向代理。

第二个坑则是协议 (protocol),这个协议决定你能用什么接口去调用 chrome 的服务,具体的 API 在这里。如何获得协议?参见 源码,因为总所周知的原因,下载协议很慢,大家先下载下来,然后在 protocol 的选项里写上:
protocol: JSON.parse(fs.readFileSync('protocol.json'))
就可以了。
ps: 作者说可以用如下这个命令生成协议,但是我生成的是一个错误的文件。。。。
chrome-remote-interface protocol -r > protocol.json


4. 远程连接

本地的连接调试非常简单,如果你想远程调用的话,需要这样子设置:
'use strict';
const CDP = require('chrome-remote-interface');
const fs = require('fs');
const protocol = JSON.parse(fs.readFileSync('protocol.json'));

const options = {
  host: "https://chrome.com", // 域名或者 IP 都行
  port: 443, // 按照实际情况来
  remote: true, // 远程连接需要设置为 true
  protocol, // client 可以使用的方法
  secure: true // https 的话需要这是这个
};

function *test() {
  const client = yield CDP(options);
  console.log('success!');
  client.close();
}
client 提供了非常多的接口,比如操作页面的 Page,有兴趣的同学可以和我们石墨团队深入浅出的交流一下。