Chrome แบบ Headless ไม่เปิดขึ้นใน Windows
นโยบาย Chrome บางรายการอาจบังคับใช้ Chrome หรือ Chromium กับส่วนขยายบางรายการ
Puppeteer ส่ง Flag --disable-extensions
โดยค่าเริ่มต้น ดังนั้นจึงไม่สามารถเปิดใช้ได้เมื่อนโยบายดังกล่าวทำงานอยู่
ในการแก้ปัญหานี้ ให้ลองเรียกใช้โดยไม่มี Flag:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});
บริบท: ปัญหา 3681
Chrome แบบ Headless ไม่เปิดตัวบน UNIX
ตรวจสอบว่าได้ติดตั้งทรัพยากร Dependency ที่จำเป็นทั้งหมดแล้ว คุณสามารถเรียกใช้ ldd chrome | grep not
บนเครื่อง Linux ได้เพื่อตรวจสอบว่าทรัพยากร Dependency ใดหายไป
ทรัพยากร 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
ทรัพยากร Dependency ของ 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 แบบ Headless ปิดใช้การประสาน 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 จริงๆ คุณสามารถเปิด Chrome ด้วยอาร์กิวเมนต์ --no-sandbox
ได้ดังนี้
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
การกำหนดค่าแซนด์บ็อกซ์ใน Chromium ทำได้ 2 วิธี
[แนะนำ] เปิดใช้การโคลนเนมสเปซของผู้ใช้
การโคลนเนมสเปซของ Sser ใช้ได้กับเคอร์เนลสมัยใหม่เท่านั้น โดยทั่วไปเนมสเปซของผู้ใช้ที่ไม่มีสิทธิ์มักจะเปิดใช้ได้ แต่สามารถเปิดพื้นที่การโจมตีเคอร์เนลได้มากขึ้นสำหรับกระบวนการที่ไม่ใช่รูท (ไม่ใช่แซนด์บ็อกซ์) เพื่อยกระดับสิทธิ์เคอร์เนล
sudo sysctl -w kernel.unprivileged_userns_clone=1
[ทางเลือก] ตั้งค่าแซนด์บ็อกซ์ของ setuid
แซนด์บ็อกซ์ของ setuid เป็นไฟล์ปฏิบัติการแบบสแตนด์อโลนและอยู่ถัดจาก Chromium ที่ Puppeteer ดาวน์โหลด คุณจะนำไฟล์ดำเนินการของแซนด์บ็อกซ์เดียวกันมาใช้ซ้ำสำหรับ 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
คุณอาจต้องส่งออกตัวแปร env CHROME_DEVEL_SANDBOX
โดยค่าเริ่มต้น ในกรณีนี้ ให้เพิ่มค่าต่อไปนี้ลงใน ~/.bashrc
หรือ .zshenv
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
วิ่งหุ่นเชิดใน Travis CI
เราทำการทดสอบ Puppeteer บน Travis CI จนถึงเวอร์ชัน 6.0.0 หลังจากนั้นเราก็ย้ายข้อมูลไปยัง GitHub Actions คุณดู .travis.yml
(v5.5.0) เป็นข้อมูลอ้างอิงได้
ตัวอย่างของแนวทางปฏิบัติแนะนำมีดังนี้
- ควรเปิดบริการ xvfb เพื่อเรียกใช้ Chromium ในโหมดที่ไม่มีส่วนหัว
- ทำงานบน Xenial Linux บน Travis ได้โดยค่าเริ่มต้น
- เรียกใช้
npm install
โดยค่าเริ่มต้น node_modules
ถูกแคชไว้โดยค่าเริ่มต้น
.travis.yml
อาจมีลักษณะเช่นนี้
language: node_js
node_js: node
services: xvfb
script:
- npm run test
เรียกใช้ Puppeteer ใน CircleCI
- เริ่มต้นด้วยอิมเมจ NodeJS ในการกำหนดค่า
yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
- ทรัพยากร Dependency อย่าง
libXtst6
อาจต้องติดตั้งด้วยapt-get
ดังนั้นให้ใช้ Threetreeslight/puppeteer orb (instructions) หรือวางส่วนต่างๆ ของแหล่งที่มาลงในการกำหนดค่าของคุณเอง - สุดท้าย หากใช้ Puppeteer ผ่าน Jest คุณอาจพบข้อผิดพลาดในการสร้างโปรเซสย่อย:
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
การทำให้ Chrome แบบไม่มีส่วนหัวทำงานใน Docker อาจเป็นเรื่องยาก Chromium ที่รวมอยู่ในชุดที่ Puppeteer ติดตั้งไม่มีทรัพยากร Dependency ของไลบรารีที่ใช้ร่วมกันที่จำเป็น
ในการแก้ไขปัญหา คุณจะต้องติดตั้งทรัพยากร Dependency ที่ขาดหายไปและแพ็กเกจ Chromium ล่าสุดใน Dockerfile ดังนี้
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 ที่แสดงวิธีเรียกใช้ Dockerfile นี้จากเว็บเซิร์ฟเวอร์ที่ทำงานบน App Engine Flex (Node)
วิ่งบนเทือกเขาแอลป์
แพ็กเกจ Chromium ใหม่ล่าสุด ที่รองรับใน Alpine คือ 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 จะเรียกใช้คอนเทนเนอร์ที่มีพื้นที่หน่วยความจำที่ใช้ร่วมกันของ /dev/shm
ที่ 64 MB
ซึ่งโดยปกติจะมีขนาดเล็กเกินไปสำหรับ 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
เป็นผู้ใช้ที่ไม่มีสิทธิ์ จึงอาจไม่มีสิทธิ์ที่จำเป็นทั้งหมด
dumb-init ก็คุ้มค่าที่จะดูนะคะ หากคุณ
ประสบกับกระบวนการเกี่ยวกับซอมบี้มากมายใน Chrome ที่ยังรออยู่ กระบวนการใช้ PID=1
มีการดูแลเป็นพิเศษ ซึ่งทำให้สิ้นสุด Chrome อย่างถูกต้องได้ยากในบางกรณี (เช่น เมื่อใช้ Docker)
วิ่งเชิดหุ่นกระบอกบนก้อนเมฆ
ใน Google App Engine
รันไทม์ Node.js ของสภาพแวดล้อมมาตรฐานของ App Engine มาพร้อมกับแพ็กเกจระบบทั้งหมดที่จำเป็นต่อการเรียกใช้ Chrome แบบ Headless
หากต้องการใช้ puppeteer
ให้ระบุโมดูลนี้เป็นทรัพยากร Dependency ใน package.json
และทำให้ใช้งาน Google App Engine ได้ อ่านเพิ่มเติมเกี่ยวกับการใช้ puppeteer
บน App Engine โดยทำตามบทแนะนำอย่างเป็นทางการ
บน Google Cloud Function
รันไทม์ Node.js 10 ของ Google Cloud Functions มาพร้อมกับแพ็กเกจระบบทั้งหมดที่จำเป็นต่อการเรียกใช้ Chrome แบบ Headless
หากต้องการใช้ puppeteer
ให้ระบุโมดูลเป็นทรัพยากร Dependency ใน package.json
และทำให้ฟังก์ชันใช้งานได้ใน Google Cloud Functions โดยใช้รันไทม์ nodejs10
เรียกใช้ Puppeteer บน Google Cloud Run
รันไทม์ Node.js เริ่มต้นของ Google Cloud Run ไม่ได้มาพร้อมกับแพ็กเกจระบบที่จำเป็นต่อการเรียกใช้ Chrome แบบ Headless ตั้งค่า Dockerfile
ของคุณเองและรวมทรัพยากร Dependency ที่ขาดหายไป
บน Heroku
การเรียกใช้ Puppeteer บน Heroku ต้องใช้ทรัพยากร Dependency เพิ่มเติมบางอย่างที่ไม่มีให้ในกล่อง Linux ที่ Heroku สร้างให้คุณ หากต้องการเพิ่มทรัพยากร Dependency ในการทำให้ใช้งานได้ ให้เพิ่มชุดบิลด์ Puppeteer Heroku ลงในรายการชุดบิลด์สำหรับแอปในส่วนการตั้งค่า > Buildpack
URL สำหรับชุดบิลด์คือ https://github.com/jontewks/puppeteer-heroku-buildpack
ตรวจสอบว่าคุณใช้โหมด '--no-sandbox'
เมื่อเปิด Puppeteer ซึ่งทำได้โดยการส่งต่อเป็นอาร์กิวเมนต์ไปยังการเรียก .launch()
ของคุณ: puppeteer.launch({ args: ['--no-sandbox'] });
เมื่อคุณคลิกเพิ่มชุดการสร้าง ให้วาง URL นั้นลงในอินพุต แล้วคลิกบันทึก ในการปรับใช้ครั้งถัดไป แอปของคุณจะติดตั้งทรัพยากร Dependency ที่ Puppeteer ต้องการเรียกใช้ด้วย
หากต้องการแสดงผลอักขระภาษาจีน ญี่ปุ่น หรือเกาหลี คุณอาจต้องใช้ชุดบิลด์ที่มีไฟล์แบบอักษรเพิ่มเติม เช่น https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
นอกจากนี้ยังมีคำแนะนำจาก @timleland อีก 1 รายการที่มีโปรเจ็กต์ตัวอย่าง
บน AWS Lambda
ขีดจำกัดของแพ็กเกจการติดตั้งใช้งาน AWS Lambda อยู่ที่ประมาณ 50 MB สิ่งนี้นำมาซึ่งความท้าทายสำหรับการวิ่ง Chrome แบบ Headless (ซึ่งแน่นอนว่าเป็น Puppeteer) ใน Lambda ชุมชนได้รวบรวมแหล่งข้อมูล จำนวนหนึ่งที่ช่วยแก้ปัญหานี้
- Chromium Binary สำหรับ AWS Lambda และ Google Cloud Functions (อัปเดตไว้ด้วย Puppeteer เวอร์ชันเสถียรล่าสุด)
- Chrome/Chromium บน AWS Lambda จาก Marco Lüthy เป็นปลั๊กอินแบบ Serverless และล้าสมัย
อินสแตนซ์ AWS EC2 ที่เรียกใช้ Amazon-Linux
หากคุณมีอินสแตนซ์ EC2 ที่เรียกใช้ amazon-linux ในไปป์ไลน์ CI/CD และต้องการเรียกใช้การทดสอบ Puppeteer ใน amazon-linux ให้ทำตามขั้นตอนต่อไปนี้
หากต้องการติดตั้ง Chromium ก่อนอื่นคุณต้องเปิดใช้
amazon-linux-extras
ซึ่งเป็นส่วนหนึ่งของ EPEL (แพ็กเกจเสริมสำหรับ Enterprise Linux)sudo amazon-linux-extras install epel -y
ขั้นตอนถัดไป ให้ติดตั้ง Chromium โดยทำดังนี้
sudo yum install -y chromium
ตอนนี้ Puppeteer สามารถเปิด Chromium เพื่อทำการทดสอบได้ หากคุณไม่เปิดใช้ EPEL และติดตั้ง Chromium ต่อไปโดยเป็นส่วนหนึ่งของ npm install
Puppeteer ไม่สามารถเปิด Chromium ได้เนื่องจาก libatk-1.0.so.0
และแพ็กเกจอื่นๆ ไม่พร้อมใช้งาน
ปัญหาการเปลี่ยนรูปแบบโค้ด
หากคุณกำลังใช้ตัวเปลี่ยนรูปแบบ JavaScript เช่น Babel หรือ TypeScript การเรียกใช้ evaluate()
ด้วยฟังก์ชันอะซิงโครนัสอาจไม่ทำงาน เนื่องจากในขณะที่ puppeteer
ใช้ Function.prototype.toString()
ในการเรียงลําดับฟังก์ชันในขณะที่ตัวแปลงสัญญาณอาจเปลี่ยนโค้ดเอาต์พุตในลักษณะที่เข้ากันไม่ได้กับ puppeteer
วิธีแก้ปัญหานี้คือการบอกกับตัวแปลงรหัสว่าอย่ายุ่งกับโค้ด เช่น กำหนดค่า TypeScript ให้ใช้ ecma เวอร์ชันล่าสุด ("target": "es2018"
) วิธีแก้ปัญหาเฉพาะหน้าคือการใช้เทมเพลตสตริง
แทนฟังก์ชันต่างๆ ดังนี้
await page.evaluate(`(async() => {
console.log('1');
})()`);