無頭 Chrome 無法在 Windows 中啟動
部分 Chrome 政策可能會強制透過特定擴充功能執行 Chrome 或 Chromium。
根據預設,Puppeteer 會傳送 --disable-extensions
標記,因此在啟用這些政策時,無法啟動。
如要解決這個問題,請嘗試在沒有旗標的情況下執行:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});
背景資訊:問題 3681。
無頭 Chrome 無法在 UNIX 上啟動
請確認已安裝所有必要的依附元件。您可以在 Linux 機器上執行 ldd chrome | grep not
,查看缺少的依附元件。
Debian (Ubuntu) 依附元件
ca-certificates
fonts-liberation
libappindicator3-1
libasound2
libatk-bridge2.0-0
libatk1.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgbm1
libgcc1
libglib2.0-0
libgtk-3-0
libnspr4
libnss3
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
lsb-release
wget
xdg-utils
CentOS 依附元件
alsa-lib.x86_64
atk.x86_64
cups-libs.x86_64
gtk3.x86_64
ipa-gothic-fonts
libXcomposite.x86_64
libXcursor.x86_64
libXdamage.x86_64
libXext.x86_64
libXi.x86_64
libXrandr.x86_64
libXScrnSaver.x86_64
libXtst.x86_64
pango.x86_64
xorg-x11-fonts-100dpi
xorg-x11-fonts-75dpi
xorg-x11-fonts-cyrillic
xorg-x11-fonts-misc
xorg-x11-fonts-Type1
xorg-x11-utils
安裝依附元件後,您必須使用這個指令更新 nss 程式庫
yum update nss -y
查看討論:
無頭 Chrome 會停用 GPU 合成功能
Chrome 和 Chromium 需要 --use-gl=egl
才能啟用無頭模式下的 GPU 加速功能。
const browser = await puppeteer.launch({
headless: true,
args: ['--use-gl=egl'],
});
已下載 Chrome,但無法在 Node.js 上啟動
如果嘗試啟動 Chromium 時出現類似下方的錯誤訊息:
(node:15505) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!
spawn /Users/.../node_modules/puppeteer/.local-chromium/mac-756035/chrome-mac/Chromium.app/Contents/MacOS/Chromium ENOENT
這表示瀏覽器已下載,卻無法正確擷取。最常見的原因是 Node.js v14.0.0 錯誤造成 extract-zip
損壞,而 Puppeteer 使用模組將瀏覽器下載內容擷取到正確位置。已在 Node.js v14.1.0 中修正錯誤,因此請確保您執行的是該版本或更高版本。
設定 Chrome Linux 沙箱
為了保護主機環境不受不受信任的網路內容影響,Chrome 會使用多層沙箱機制。
主機必須先設定主機,這項功能才能正常運作。如果沒有適合 Chrome 使用的優質沙箱,就會異常終止並傳回 No usable sandbox!
錯誤。
如果您完全信任自己在 Chrome 中開啟的內容,可以使用 --no-sandbox
引數啟動 Chrome:
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
在 Chromium 中設定沙箱的方法有 2 種。
[建議] 啟用使用者命名空間複製功能
只有新式核心支援Sser 命名空間複製。未獲授權的使用者命名空間通常可啟用,但可能會針對 (未採用沙箱機制) 的非根程序開啟更多核心攻擊途徑,提升核心權限。
sudo sysctl -w kernel.unprivileged_userns_clone=1
[替代] 設定 setuid 沙箱
setuid 沙箱可做為獨立執行檔,位於 Puppeteer 下載的 Chromium 旁。您可以為不同的 Chromium 版本重複使用相同的沙箱執行檔,因此以下程式碼在每個主機環境只能執行一次:
# cd to the downloaded instance
cd <project-dir-path>/node_modules/puppeteer/.local-chromium/linux-<revision>/chrome-linux/
sudo chown root:root chrome_sandbox
sudo chmod 4755 chrome_sandbox
# copy sandbox executable to a shared location
sudo cp -p chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
# export CHROME_DEVEL_SANDBOX env variable
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
建議您預設匯出 CHROME_DEVEL_SANDBOX
環境變數。在這種情況下,請將以下內容新增至 ~/.bashrc
或 .zshenv
:
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
在 Travis CI 上執行 Puppeteer
我們於 6.0.0 版之前針對 Puppeteer 執行 Puppeteer 測試,直到 6.0.0 版為止,之後才會遷移至 GitHub Actions。如需參考,請參閱 .travis.yml
(5.5.0 版)。
以下列舉幾個最佳做法:
- 必須啟動 xvfb 服務,才能在無頭模式下執行 Chromium
- 預設在 Travis 上執行 Xenial Linux
- 預設執行
npm install
- 預設會快取
node_modules
.travis.yml
可能看起來會像這樣:
language: node_js
node_js: node
services: xvfb
script:
- npm run test
在 CircleCI 上執行 Puppeteer
- 從設定中的 NodeJS 映像檔開始。
yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
- 您可能需要使用
apt-get
等依附元件 (例如libXtst6
) 安裝,因此請使用 3treeslight/puppeteer 或 Bb (instructions),或是將其來源的部分內容貼到自己的設定中。 - 最後,如果您在透過 Jest 使用 Puppeteer,可能會遇到產生子項程序時發生錯誤:
shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11)
這可能是由於 Jest 自動偵測:在整部機器 (36
) 上的程序數量,而非容器允許的數量 (2
)。如要修正這個問題,請在測試指令中設定jest --maxWorkers=2
。
在 Docker 中執行 Puppeteer
在 Docker 中開啟無頭 Chrome 並不容易。Puppeteer 安裝的隨附的 Chromium 缺少必要的共用程式庫依附元件。
如要修正這個問題,您需要在 Dockerfile 中安裝缺少的依附元件和最新的 Chromium 套件:
FROM node:14-slim
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# installs, work.
RUN apt-get update \
&& apt-get install -y wget gnupg \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise
# uncomment the following lines to have `dumb-init` as PID 1
# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_x86_64 /usr/local/bin/dumb-init
# RUN chmod +x /usr/local/bin/dumb-init
# ENTRYPOINT ["dumb-init", "--"]
# Uncomment to skip the chromium download when installing puppeteer. If you do,
# you'll need to launch puppeteer with:
# browser.launch({executablePath: 'google-chrome-stable'})
# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
# Install puppeteer so it's available in the container.
RUN npm init -y && \
npm i puppeteer \
# Add user so we don't need --no-sandbox.
# same layer as npm install to keep re-chowned files from using up several hundred MBs more space
&& groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
&& mkdir -p /home/pptruser/Downloads \
&& chown -R pptruser:pptruser /home/pptruser \
&& chown -R pptruser:pptruser /node_modules \
&& chown -R pptruser:pptruser /package.json \
&& chown -R pptruser:pptruser /package-lock.json
# Run everything after as non-privileged user.
USER pptruser
CMD ["google-chrome-stable"]
建構容器:
docker build -t puppeteer-chrome-linux .
透過將 node -e "<yourscript.js content as a string>"
做為指令傳遞,以執行容器:
docker run -i --init --rm --cap-add=SYS_ADMIN \
--name puppeteer-chrome puppeteer-chrome-linux \
node -e "`cat yourscript.js`"
我們在 https://github.com/ebidel/try-puppeteer 有完整範例,說明如何從在 App Engine Flex (Node) 上執行的網路伺服器執行這個 Dockerfile。
在阿爾卑斯山跑步
Alpine 支援的最新 Chromium 套件為 100,與 Puppeteer v13.5.0 相對應。
Dockerfile 範例:
FROM alpine
# Installs latest Chromium (100) package.
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont \
nodejs \
yarn
...
# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Puppeteer v13.5.0 works with Chromium 100.
RUN yarn add puppeteer@13.5.0
# Add user so we don't need --no-sandbox.
RUN addgroup -S pptruser && adduser -S -G pptruser pptruser \
&& mkdir -p /home/pptruser/Downloads /app \
&& chown -R pptruser:pptruser /home/pptruser \
&& chown -R pptruser:pptruser /app
# Run everything after as non-privileged user.
USER pptruser
...
Docker 的最佳做法
根據預設,Docker 會執行具有 64 MB /dev/shm
共用記憶體空間的容器。這對 Chrome 而言通常過小,並會導致 Chrome 在轉譯大型網頁時異常終止。如要修正,請使用 docker run --shm-size=1gb
執行容器,增加 /dev/shm
的大小。自 Chrome 65 版起,現在已不再需要使用。請改為使用 --disable-dev-shm-usage
標記啟動瀏覽器:
const browser = await puppeteer.launch({
args: ['--disable-dev-shm-usage'],
});
這會將共用記憶體檔案寫入 /tmp
,而不是 /dev/shm
。查看 crbug.com/736452。
啟動 Chrome 時,畫面是否出現其他怪異錯誤?在本機開發時,請嘗試使用 docker run --cap-add=SYS_ADMIN
執行容器。由於 Dockerfile 將 pptr
使用者新增為非特殊權限使用者,因此可能無法擁有所有必要權限。
如果您發現多個 Chrome 程序持續執行,建議您確認 dumb-init。對使用 PID=1
的程序進行特殊處理,在某些情況下 (例如使用 Docker) 很難正確終止 Chrome。
在雲端執行 Puppeteer
在 Google App Engine 上
App Engine Standard 環境的 Node.js 執行階段隨附執行 Headless Chrome 所需的所有系統套件。
如要使用 puppeteer
,請在 package.json
中將模組列為依附元件,然後部署至 Google App Engine。請參閱官方教學課程,進一步瞭解如何在 App Engine 上使用 puppeteer
。
在 Google Cloud Functions 上
Google Cloud Functions 的 Node.js 10 執行階段隨附執行 Headless Chrome 所需的所有系統套件。
如要使用 puppeteer
,請在 package.json
中將模組列為依附元件,然後使用 nodejs10
執行階段將函式部署至 Google Cloud Functions。
在 Google Cloud Run 中執行 Puppeteer
Google Cloud Run 的預設 Node.js 執行階段不包含執行 Headless Chrome 所需的系統套件。設定自己的 Dockerfile
並加入缺少的依附元件。
在 Heroku 上
在 Heroku 上執行 Puppeteer 需要 Heroku 為您啟動的 Linux 包裝盒中不包含某些額外的依附元件。如要在部署時新增依附元件,請依序前往「設定」>「Buildpacks」,將 Puppeteer Heroku 建構包新增至應用程式的建構包清單。
建構包的網址為 https://github.com/jontewks/puppeteer-heroku-buildpack
請務必在啟動 Puppeteer 時使用 '--no-sandbox'
模式。實作方法是將引數做為引數傳遞至 .launch()
呼叫:puppeteer.launch({ args: ['--no-sandbox'] });
。
點按「新增建構包」後,請將該網址貼到輸入欄位中,然後按一下「儲存」。在下次部署時,您的應用程式也會安裝 Puppeteer 必須執行的依附元件。
如要轉譯中文、日文或韓文字元,您可能需要使用 buildpack 搭配其他字型檔案,例如 https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
另外,還有一個 @timleland 提供的指南,其中包含專案範例。
在 AWS Lambda 中
AWS Lambda 的部署套件大小限制到 50 MB 以內。在 Lambda 上執行無頭 Chrome (以及 Puppeteer) 時,這會造成挑戰。這個社群彙整了幾項針對此問題提供的資源:
- AWS Lambda 和 Google Cloud Functions 適用的 Chromium 二進位檔 (採用 Puppeteer 的最新穩定版本更新)
- Marco Lüthy 的 AWS Lambda 上的 Chrome/Chromium 是無伺服器的外掛程式,已過時。
執行 Amazon-Linux 的 AWS EC2 執行個體
如果您的 CI/CD 管道中有執行 amazon-linux 的 EC2 執行個體,並且想要在 amazon-linux 中執行 Puppeteer 測試,請按照下列步驟操作。
如要安裝 Chromium,您必須先啟用
amazon-linux-extras
,這是 EPEL (Enterprise Linux 適用的額外套件) 的一部分:sudo amazon-linux-extras install epel -y
接著,請安裝 Chromium:
sudo yum install -y chromium
現在,Puppeteer 可以啟動 Chromium 來執行測試。如果您並未啟用 EPEL 並繼續安裝 npm install
的 Chromium,由於 libatk-1.0.so.0
和更多套件無法使用,Puppeteer 無法啟動 Chromium。
程式碼轉譯問題
如果您使用的是 babel 或 TypeScript 等 JavaScript 轉譯器,可能就無法使用非同步函式呼叫 evaluate()
。這是因為雖然 puppeteer
使用 Function.prototype.toString()
將函式序列化,而傳輸工具可能會變更輸出程式碼,因而與 puppeteer
不相容。
要解決這個問題,其中一種解決方法是指示轉譯器不要混亂程式碼,例如將 TypeScript 設定為使用最新的 Eecma 版本 ("target": "es2018"
)。另一個解決方法是使用字串範本,而不是函式:
await page.evaluate(`(async() => {
console.log('1');
})()`);