优雅地安装 Puppeteer

Puppeteer 是一个 Node.js 库,用于操作 Chromium 无头浏览器,可以用来做爬虫、后台渲染等,用途很广。但是,由于它依赖了 Chromium,所以体积很大,而且每次安装需要临时下载内核,在国内安装起来不太方便。

总结一下,Puppeteer 存在的问题:

  • 网络问题: 官方源下载很慢。
  • 体积太大: 每次安装一个 Puppeteer 就相当于安装了一个 Chromium,大几百MB就没了,基本上使用了 Puppeteer 后的项目 node_modules 大小都以 GB 计,我 256GB 的磁盘上安装几个 Node.js 项目,硬盘就满了。

网络问题

先看网络问题,好在中国用户量大,官方也早早地提供了使用镜像的能力,只需配置环境变量如下:

export PUPPETEER_DOWNLOAD_HOST=https://npm.taobao.org/mirrors

即可解决下载慢的问题。

体积问题

体积问题就不太好解决了,总不能每次用完了都把 node_modules 删掉。

跳过下载

官方提供了一个环境变量 PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 用于跳过 Chromium 内核的下载。很多情况下,可能只是依赖的依赖在某种特殊场景下会用到一点点 Puppeteer,它就像大森林里猴子手上的那根香蕉,被引了进来。这时我们可以通过如下配置禁止下载巨大的 Chromium 内核:

export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1

但是这样会带来一个新的问题,真正需要用到 Puppeteer 的地方会因为缺少内核而报错。

难道我们要在安装依赖的时候手动判断这次是否需要 Puppeteer,并手动指定环境变量?这种方法显然不是我们能接受的。

经过长期的网络和磁盘空间之间的斗争,我终于发现了一个更好的方法。

共享内核

实际上 Electron 也有类似的问题,早就有人提供过这样的思路,就是希望全局安装一份 Electron 的运行时,然后只需要发布各个 App 的 JavaScript 代码。然而,这种方式可能因为全局安装的 Electron 版本不匹配而导致运行时错误。

Puppeteer 是通过 DevTools 协议来操作 Chromium 内核的,所以我们可以先全局安装一个 Chromium,然后再指定所有的 Puppeteer 都使用这个 Chromium 作为无头浏览器来访问。

$ brew cask install chromium

安装完成后,通过如下命令可以查看安装信息,获取 Chromium 的真实路径:

$ brew cask info chromium

在展示出来的信息中,我们可以看到这样的真实路径:

/usr/local/Caskroom/chromium/775897/

那么 Chromium 内核的路径就可以拼出来了:

/usr/local/Caskroom/chromium/775897/chrome-mac/Chromium.app/Contents/MacOS/Chromium

然后配置环境变量,在安装时跳过 Chromium 内核的下载,并让所有的 Puppeteer 都使用这个 Chromium 内核:

export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1
export PUPPETEER_EXECUTABLE_PATH=/usr/local/Caskroom/chromium/775897/chrome-mac/Chromium.app/Contents/MacOS/Chromium

小结

通过上面的方法,我们就实现了全局共享运行时,并且跳过了不必要的内核下载,网络问题和体积问题都得到了解决。

这个问题的解决也让我对运行时有了更多的理解,记得以前用 Windows 系统,经常需要安装各种 vcruntime,觉得很麻烦。实际上正是因为这些 runtime 的存在,才让开发者们可以更轻松地和系统交互,而且发布的应用程序体积也更小。

现在磁盘空间越来越大,服务端开始追求良好的可移植性,喜欢把什么依赖都打包到一起。然而这种方式到了客户端并不是那么友好,毕竟我们一般只用一台机器,磁盘容不下这么多大项目。希望有朝一日可以看到 Electron 也从根本上解决这个问题,不用动辄下载上百兆的安装包,只为了更新几行代码。


© 2020