Chrome ללא ממשק גרפי לא מופעל ב-Windows
כללי מדיניות מסוימים של Chrome עשויים לאלץ את ההפעלה של Chrome או של Chromium עם תוספים מסוימים.
ה-Puppeteer מעביר את הדגל --disable-extensions
כברירת מחדל, ולכן הוא לא פועל
כאשר כללי מדיניות כאלה פעילים.
כדי לעקוף את הבעיה, אפשר לנסות לפעול בלי הדגל:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});
הקשר: issue 3681.
Chrome ללא ממשק גרפי לא מופעל ב-UNIX
צריך לוודא שכל יחסי התלות הנחוצים מותקנים. אפשר להריץ את ldd chrome | grep not
במחשב Linux כדי לבדוק אילו יחסי תלות חסרים.
יחסי תלות של 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 במצב 'דפדפן ללא GUI'.
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 בגרסה 14.0.0 ששבר את extract-zip
, והמודול Puppeteer משתמש בו כדי לחלץ הורדות של הדפדפן למקום הנכון. הבאג תוקן ב-Node.js בגרסה 14.1.0, לכן חשוב לוודא שאתם משתמשים בגרסה הזו ואילך.
הגדרת ארגז חול של Chrome Linux
כדי להגן על סביבת המארח מפני תוכן אינטרנט לא מהימן, ב-Chrome משתמשים בשכבות מרובות של הרצה בארגז חול.
כדי שהקטע הזה יפעל כראוי, קודם צריך להגדיר את המארח. אם אין ארגז חול (sandbox) מתאים לשימוש ב-Chrome, הוא יקרוס עם השגיאה No usable sandbox!
.
אם אתם סומכים לחלוטין על התוכן שאתם פותחים ב-Chrome, תוכלו להפעיל את Chrome באמצעות הארגומנט --no-sandbox
:
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
יש שתי דרכים להגדיר ארגז חול ב-Chromium.
[מומלץ] הפעלת שכפול של מרחב השמות של המשתמשים
שכפול מרחב שמות Sser נתמך רק בליבות מבוססות מודרניות. בדרך כלל אפשר להפעיל מרחבי שמות של משתמשים ללא הרשאות, אבל הם יכולים לפתוח שטח התקפה נוסף בליבה עבור תהליכים שאינם בארגז החול (ללא ארגז חול) כדי להעלות אותם להרשאות ליבה.
sudo sysctl -w kernel.unprivileged_userns_clone=1
[חלופה] הגדרת ארגז חול של הגדרה
ארגז החול setuid מגיע כקובץ הפעלה עצמאי וממוקם לצד Chromium שממנו ה-Puppeteer מוריד. אפשר להשתמש שוב באותו קובץ הפעלה ב-Sandbox בגרסאות שונות של 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
הפעלת Puppeteer ב-Travis CI
ביצענו את הבדיקות של Puppeteer ב-Travis CI עד גרסה 6.0.0, ולאחר מכן עברנו ל-GitHub Actions. תוכלו לעיין במאמר
.travis.yml
(גרסה 5.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`
- ככל הנראה תלויים כמו
libXtst6
צריך להתקין בעזרתapt-get
, לכן צריך להשתמש ב-hirtreeslight/puppeteer (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
בפקודת הבדיקה.
הפעילו את Puppeteer ב-Docker
לא פשוט להפעיל את Chrome ללא GUI ב-Docker. בחבילה של Chromium שמתקינים את Puppeteer חסרים יחסי התלות הנחוצים של הספרייה המשותפת.
כדי לפתור את הבעיה, צריך להתקין את יחסי התלות החסרים ואת חבילת 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 גרסה 13.5.0.
דוגמה לקובץ Docker:
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
בנפח 64MB.
השם הזה בדרך כלל קטן מדי ל-Chrome והוא יגרום ל-Chrome לקרוס במהלך רינדור דפים גדולים. כדי לפתור את הבעיה, מריצים את הקונטיינר עם docker run --shm-size=1gb
כדי להגדיל את /dev/shm
. אין יותר צורך בכך החל מגרסה 65 של Chrome. במקום זאת, הפעילו את הדפדפן עם הדגל --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
. מכיוון
שקובץ Docker מוסיף משתמש pptr
כמשתמש ללא הרשאות, יכול להיות שאין לו את כל ההרשאות הנדרשות.
dumb-init כדאי לבדוק אם נתקלת בהרבה זומבים תהליכים של Chrome שנמשכים מסביב. יש טיפול מיוחד בתהליכים ב-PID=1
, ולכן קשה לסגור את Chrome בצורה נכונה במקרים מסוימים (למשל באמצעות Docker).
מפעילים את Puppeteer בענן
ב-Google App Engine
זמן הריצה של Node.js של הסביבה הסטנדרטית של App Engine כולל את כל חבילות המערכת שדרושות להפעלת Chrome ללא ממשק גרפי.
כדי להשתמש ב-puppeteer
, צריך לרשום את המודול בתור תלות ב-package.json
ולפרוס אותו ב-Google App Engine. מידע נוסף על השימוש ב-puppeteer
ב-App Engine זמין במדריך הרשמי.
ב-Google Cloud Functions
זמן הריצה של Node.js 10 של Google Cloud Functions כולל את כל חבילות המערכת שדרושות לצורך הפעלת Chrome ללא ממשק גרפי.
כדי להשתמש ב-puppeteer
, צריך לרשום את המודול כתלות ב-package.json
ולפרוס את הפונקציה ב-Google Cloud Functions באמצעות זמן הריצה של nodejs10
.
הפעלת Puppeteer ב-Google Cloud Run
זמן הריצה של Node.js המוגדר כברירת מחדל ל-Google Cloud Run לא כולל את חבילות המערכת שדרושות להפעלת Chrome ללא ממשק גרפי. מגדירים Dockerfile
משלכם וכוללים את יחסי התלות החסרים.
על הרוקו
ההפעלה של Puppeteer ב-Heroku מצריכה יחסי תלות נוספים שלא נכללים בתיבת Linux שה-Heroku מייצר. כדי להוסיף את יחסי התלות בפריסה, מוסיפים את ה-buildpack של Puppeteer Heroku לרשימת ה-buildpacks באפליקציה בקטע 'הגדרות' > 'Buildpacks'.
כתובת ה-URL של ה-buildpack היא https://github.com/jontewks/puppeteer-heroku-buildpack
חשוב להשתמש במצב '--no-sandbox'
כשמפעילים את Puppeteer. כדי לעשות זאת, אפשר להעביר אותה כארגומנט לקריאה של .launch()
:
puppeteer.launch({ args: ['--no-sandbox'] });
.
כשלוחצים על Addpack (הוספת buildpack), מדביקים את כתובת ה-URL הזו בקלט ולוחצים על Save (שמירה). בפריסה הבאה, האפליקציה תתקין גם את יחסי התלות שדרושים להפעלת Puppeteer.
אם אתם צריכים לעבד תווים בסינית, ביפנית או בקוריאנית, ייתכן שתצטרכו להשתמש ב-buildpack עם קובצי גופנים נוספים כמו https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
יש גם מדריך נוסף מ- @timleland שכולל פרויקט לדוגמה.
ב-AWS Lambda
AWS Lambda מגביל את הגודל של חבילות הפריסה לכ-50MB. כתוצאה מכך, ההפעלה של Chrome ללא דפדפן GUI (ולכן גם Puppeteer) ב-Lambda. חברי הקהילה גיבשו כמה משאבים לפתרון הבעיות:
- Chromium Binary עבור AWS Lambda ו-Google Cloud Functions (נשמר בגרסה היציבה האחרונה של Puppeteer)
- Chrome/Chromium ב-AWS Lambda של Marco Lüthy הוא פלאגין ללא שרת והוא מיושן.
מופע של 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');
})()`);