Docker安装无头浏览器Puppeteer

排查问题流程

由于很多文件需要下载,因此先装个 wget:

1
yum install -y wget

安装 Puppeteer

注意:如果是在项目下,通过 Dockerfile 安装 Puppeteer,请务必在 Dockerfile 执行 npm install 之前,添加如下这一行,否则会因为内网无法下载 chrome 而导致 Puppeteer 安装失败:

1
export PUPPETEER_SKIE_DOWNLOAD='true'

npm 安装:

1
npm install --save puppeteer

本地 windows 环境安装后是没有问题的,但是在 centos6.9 的 docker 中安装后,使用的时候就报错了:

1
2
3
4
5
2020-09-08 15:32:28,576 ERROR 205 nodejs.unhandledRejectionError: Could not find browser revision 782078. Run "PUPPETEER_PRODUCT=firefox npm install" or "PUPPETEER_PRODUCT=firefox yarn install" to download a supported Firefox browser binary.
at ChromeLauncher.launch (/var/www/resource/server/node_modules/puppeteer/lib/cjs/puppeteer/node/Launcher.js:86:23)
name: "unhandledRejectionError"
pid: 205
hostname: iwc-datav-resource-644b9d5f45-v72jl

跟进报错位置的源码,发现是 Chrome 浏览器没装上:

1
2
3
4
5
6
7
8
let chromeExecutable = executablePath;
if (os.arch() === 'arm64') {
chromeExecutable = '/usr/bin/chromium-browser';
} else if (!executablePath) {
const { missingText, executablePath } = resolveExecutablePath(this);
if (missingText) throw new Error(missingText);
chromeExecutable = executablePath;
}

网站查了资料,说是因为缺少依赖导致的,因此把依赖给装上去:

1
2
3
4
5
#依赖库
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y

#字体
yum install ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y

然后再次执行 npm install –save puppeteer,发现 chrome 确实被安装上了

不过跑不起来,然后我又选择手动安装了 Chrome。

手动下载 Chrome

2024.11.08 历史老版本的 Chrome 下载:Google Chrome Older Versions Download (Windows, Linux & Mac)

2024.04.18 现在改为从这个网站下载:

Chrome for Testing availability

然后在 JS 中初始化 Puppeteer 的时候指定 Chrome 路径即可(注意别加错位置了,必须设置在 launch 方法的参数里面才行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const puppeteer = require('puppeteer');

(async () => {
// 错误
// puppeteer.executablePath('/usr/local/chrome-linux64/chrome');
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
// 正确
executablePath: '/usr/local/chrome-linux64/chrome',
});
const page = await browser.newPage();
await page.goto('https://t.zhouchangju.com/test/slide.html');
await page.screenshot({ path: 'example.png' });

await browser.close();
})();

下面的内容可以不用看了,已过期:

我根据这个文章,去这个网站手动下载了所需的 Chrome 版本,在项目中指定了 chrome 的路径来执行,发现还是报错:

1
2
2020-09-08 21:29:01,793 ERROR 652 nodejs.unhandledRejectionError: Failed to launch the browser process!
/var/www/resource/server/node_modules/puppeteer/.local-chromium/linux-782078/chrome-linux/chrome: error while loading shared libraries: libatk-bridge-2.0.so.0: cannot open shared object file: No such file or directory

提示很明确,就是缺少了依赖的库文件。其实 puppeteer 的官网有明确说明Docker 环境下依赖的库文件的,只是我之前没有注意。那现在就继续装依赖库吧。

安装依赖的库文件

进入 chrome 文件的目录,查看 chrome 依赖的库哪些是没有的:

1
ldd chrome|grep not

发现共有四个库是缺失的:

1
2
3
4
5
libatk-bridge-2.0.so.0 => not found
libgbm.so.1 => not found
libatspi.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found

然后查看下哪些软件有安装这些库。先从第一个 libatk-bridge-2.0.so.0 开始:

1
yum provides libatk-bridge-2.0.so.0

搜不到,但是 yum 提示我改为匹配的方式搜索:

1
yum provides */libatk-bridge-2.0.so.0

这下有了,输出的结果很多,然后我选择了符合我当前系统的版本最高的一个软件,是 firefox 的:

1
2
3
4
firefox-68.12.0-1.el6.centos.x86_64 : Mozilla Firefox Web browser
Repo : updates
Matched from:
Filename : /usr/lib64/firefox/bundled/lib64/libatk-bridge-2.0.so.0

安装一下:

1
yum install -y firefox

此时就可以看到,在/usr/lib64/firefox/bundled/lib64 这个目录下,已经有我所需要的 libatk-bridge-2.0.so.0 文件了,不仅是这个文件,其他三个也都有了,因此只需要创建软链接到系统库目录下即可:

1
2
3
4
ln -s /usr/lib64/firefox/bundled/lib64/libatk-bridge-2.0.so.0 /usr/lib64/libatk-bridge-2.0.so.0
ln -s /usr/lib64/firefox/bundled/lib64/libatspi.so.0 /usr/lib64/libatspi.so.0
ln -s /usr/lib64/firefox/bundled/lib64/libgtk-3.so.0 /usr/lib64/libgtk-3.so.0
ln -s /usr/lib64/firefox/bundled/lib64/libgdk-3.so.0 /usr/lib64/libgdk-3.so.0

然后我们再跑一下程序,发现报如下错误:

1
2
3
4
2020-09-08 22:12:26,190 ERROR 1107 nodejs.unhandledRejectionError: Failed to launch the browser process!
/var/www/resource/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /var/www/resource/chrome-linux/chrome)
/var/www/resource/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by /var/www/resource/chrome-linux/chrome)
/var/www/resource/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.16' not found (required by /var/www/resource/chrome-linux/chrome)

是因为 glicb 版本过低导致的(本地是 2.12 版本),那就升级吧。

升级 glicb

查看 glibc 版本的命令:

1
/lib64/libc.so.6

根据 stackoverflow 的这个回答进行升级:

1
2
3
4
5
6
7
8
9
10
11
mkdir ~/glibc_install
cd ~/glibc_install
curl http://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.gz -O glibc-2.14.tar.gz
tar zxvf glibc-2.14.tar.gz
cd glibc-2.14
mkdir build
cd build
../configure --prefix=/opt/glibc-2.14
make -j4
make install
export LD_LIBRARY_PATH=/opt/glibc-2.14/lib

这个过程需要花个几分钟(主要是 make 耗时较长)。

然后再次启动程序,继续报错:

1
2
3
2020-09-08 22:46:25,864 ERROR 28799 nodejs.unhandledRejectionError: Failed to launch the browser process!
/var/www/resource/chrome-linux/chrome: /opt/glibc-2.14/lib/libc.so.6: version `GLIBC_2.15' not found (required by /var/www/resource/chrome-linux/chrome)
/var/www/resource/chrome-linux/chrome: /opt/glibc-2.14/lib/libc.so.6: version `GLIBC_2.16' not found (required by /var/www/resource/chrome-linux/chrome)

还需要安装 15 和 16 版本,继续安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mkdir ~/glibc_install
cd ~/glibc_install
curl http://ftp.gnu.org/gnu/glibc/glibc-2.15.tar.gz -O glibc-2.15.tar.gz
tar zxvf glibc-2.15.tar.gz
cd glibc-2.15
mkdir build
cd build
../configure --prefix=/opt/glibc-2.15
make -j4
make install
export LD_LIBRARY_PATH=/opt/glibc-2.15/lib

mkdir ~/glibc_install
cd ~/glibc_install
curl http://ftp.gnu.org/gnu/glibc/glibc-2.16.0.tar.gz -O glibc-2.16.tar.gz
tar zxvf glibc-2.16.tar.gz
cd glibc-2.16
mkdir build
cd build
../configure --prefix=/opt/glibc-2.16
make -j4
make install
export LD_LIBRARY_PATH=/opt/glibc-2.16/lib

装好了 15,在装 16 的过程中,我启动了程序想试试,结果迎来了:

1
/usr/bin/env: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument

先修正:

1
export LD_LIBRARY_PATH=/usr/local/lib

猜测:是不是 CentOS6 无法安装高于 2.14 的 glibc?在stackoverflow找到一个人,遇到了跟我同样的问题。然后我找到一个通过 RPM 安装的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/sh

# update glibc to 2.17 for CentOS 6

wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-common-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-devel-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-headers-2.17-55.el6.x86_64.rpm

rpm -Uvh glibc-2.17-55.el6.x86_64.rpm \
glibc-common-2.17-55.el6.x86_64.rpm \
glibc-devel-2.17-55.el6.x86_64.rpm \
glibc-headers-2.17-55.el6.x86_64.rpm

安装过程报错,缺少 glibc 的 2.14 版本,不用管。

装好后,启动程序,继续报错:

1
2
2020-09-08 15:31:41,150 ERROR 23251 nodejs.unhandledRejectionError: Failed to launch the browser process!
/var/www/resource/chrome-linux/chrome: symbol lookup error: /usr/lib64/firefox/bundled/lib64/libcairo-gobject.so.2: undefined symbol: cairo_region_destroy

解决方法:

1
LD_LIBRARY_PATH=/usr/local/lib:/usr/lib64/firefox/bundled/lib64

继续报错:

1
2
2020-09-08 15:39:19,212 ERROR 23318 nodejs.unhandledRejectionError: Failed to launch the browser process!
/var/www/resource/chrome-linux/chrome: symbol lookup error: /var/www/resource/chrome-linux/chrome: undefined symbol: dbus_validate_bus_name

网上说可能是 firefox 版本太高了,可以回退到 60.0.1 版本试试。

我之前是通过 yum 安装的 firefox,现在得从官网下载手动安装下。

https://download-installer.cdn.mozilla.net/pub/firefox/releases/60.0.1/linux-x86_64/en-US/firefox-60.0.1.tar.bz2

明天来安装这个,文件我已经拷贝到/home/share 了

/home/share/firefox-60.0.1.tar

还是不行

抽象问题,改为在 CentOS6 安装 Chrome。

根据这个文章进行安装:

找包:

https://centos.pkgs.org/6/nux-dextop-x86_64/chrome-deps-stable-3.11-1.x86_64.rpm.html

1
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm

官方教程:

https://www.itzgeek.com/how-tos/linux/centos-how-tos/how-to-install-google-chrome-17-on-fedora-16-verne.html

官方的也不行,依赖库版本太低了。感觉我还是升级系统吧,不然 chrome 版本过低,兼容性也会是个问题。

Note: Google Chrome can not be run as root

Puppeteer 常用操作

抓取页面

1
2
3
4
5
6
7
8
9
10
11
12
const puppeteer = require('puppeteer');

(async () => {
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.goto('https://t.zhouchangju.com/test/slide.html');
await page.screenshot({ path: 'example.png' });

await browser.close();
})();

禁用缓存

1
await page.setCacheEnabled(false);

解决中文字体乱码问题

https://he5050.github.io/post/ce-shi-2/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 装工具
yum -y install fontconfig
yum -y install ttmkfdir


mkdir /usr/share/fonts/chinese
# 传字体文件

chmod -R 775 /usr/share/fonts/chinese


ttmkfdir -e /usr/share/X11/fonts/encodings/encodings.dir
vi /etc/fonts/fonts.conf
# 添加一个<dir>/usr/share/fonts/chinese</dir>字体目录
<dir>/usr/share/X11/fonts/Type1</dir> <dir>/usr/share/X11/fonts/TTF</dir> <dir>/usr/local/share/fonts</dir><dir>/usr/share/fonts/chinese</dir>

fc-cache

一些技巧

将问题抽象为核心问题

比如这次,其实拆分出来,就是怎么在 CentOS6 安装 Chrome。我因为跟着报错走,一直都是被牵着鼻子走,已经迷失了问题的本质了。

1
yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm

查看某个软件缺少哪些库

以 chrome 为例:

1
ldd chrome|grep not

查看哪些软件依赖了某个库

1
yum provides */libatk-bridge-2.0.so.0

然后安装一个合适的软件,并复制库文件到/usr/lib64/目录下,就可以解决库文件缺失的问题了。

找 RPM 包的网站

https://rpmfind.net/linux/rpm2html/search.php

安装 Chrome 的脚本

1
curl https://intoli.com/install-google-chrome.sh | bash

一些资料

Puppeteer 和 Chromium 的版本对应关系

https://pptr.nodejs.cn/chromium-support

查看 Chrome 与 Chromium 的版本对应关系

Chromium Dash

找到浏览器版本,点进去,可以看到 commit 信息,再点进去,里面的Cr-Branched-Form末尾的main的版本,就是对应的 Chromium 的版本。

下载 Chrome

Chrome for Testing availability