Docker安装无头浏览器Puppeteer
排查问题流程
由于很多文件需要下载,因此先装个 wget:
yum install -y wget
安装 Puppeteer
注意:如果是在项目下,通过 Dockerfile 安装 Puppeteer,请务必在 Dockerfile 执行 npm install 之前,添加如下这一行,否则会因为内网无法下载 chrome 而导致 Puppeteer 安装失败:
export PUPPETEER_SKIE_DOWNLOAD='true'
npm 安装:
npm install --save puppeteer
本地 windows 环境安装后是没有问题的,但是在 centos6.9 的 docker 中安装后,使用的时候就报错了:
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 浏览器没装上:
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;
}
网站查了资料,说是因为缺少依赖导致的,因此把依赖给装上去:
#依赖库
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 方法的参数里面才行):
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 的路径来执行,发现还是报错:
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 依赖的库哪些是没有的:
ldd chrome|grep not
发现共有四个库是缺失的:
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 开始:
yum provides libatk-bridge-2.0.so.0
搜不到,但是 yum 提示我改为匹配的方式搜索:
yum provides */libatk-bridge-2.0.so.0
这下有了,输出的结果很多,然后我选择了符合我当前系统的版本最高的一个软件,是 firefox 的:
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
安装一下:
yum install -y firefox
此时就可以看到,在/usr/lib64/firefox/bundled/lib64 这个目录下,已经有我所需要的 libatk-bridge-2.0.so.0 文件了,不仅是这个文件,其他三个也都有了,因此只需要创建软链接到系统库目录下即可:
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
然后我们再跑一下程序,发现报如下错误:
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 版本的命令:
/lib64/libc.so.6
根据 stackoverflow 的这个回答进行升级:
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 耗时较长)。
然后再次启动程序,继续报错:
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 版本,继续安装:
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 的过程中,我启动了程序想试试,结果迎来了:
/usr/bin/env: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument
先修正:
export LD_LIBRARY_PATH=/usr/local/lib
猜测:是不是 CentOS6 无法安装高于 2.14 的 glibc?在stackoverflow找到一个人,遇到了跟我同样的问题。然后我找到一个通过 RPM 安装的方法:
#! /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 版本,不用管。
装好后,启动程序,继续报错:
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
解决方法:
LD_LIBRARY_PATH=/usr/local/lib:/usr/lib64/firefox/bundled/lib64
继续报错:
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,现在得从官网下载手动安装下。
明天来安装这个,文件我已经拷贝到/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
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
官方教程:
官方的也不行,依赖库版本太低了。感觉我还是升级系统吧,不然 chrome 版本过低,兼容性也会是个问题。
Note: Google Chrome can not be run as root
Puppeteer 常用操作
抓取页面
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();
})();
禁用缓存
await page.setCacheEnabled(false);
解决中文字体乱码问题
https://he5050.github.io/post/ce-shi-2/
# 装工具
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。我因为跟着报错走,一直都是被牵着鼻子走,已经迷失了问题的本质了。
yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
查看某个软件缺少哪些库
以 chrome 为例:
ldd chrome|grep not
查看哪些软件依赖了某个库
yum provides */libatk-bridge-2.0.so.0
然后安装一个合适的软件,并复制库文件到/usr/lib64/目录下,就可以解决库文件缺失的问题了。
找 RPM 包的网站
https://rpmfind.net/linux/rpm2html/search.php
安装 Chrome 的脚本
curl https://intoli.com/install-google-chrome.sh | bash
一些资料
Puppeteer 和 Chromium 的版本对应关系
https://pptr.nodejs.cn/chromium-support
查看 Chrome 与 Chromium 的版本对应关系
找到浏览器版本,点进去,可以看到 commit 信息,再点进去,里面的Cr-Branched-Form末尾的main的版本,就是对应的 Chromium 的版本。