Chrome bez interfejsu graficznego nie uruchamia się w systemie Windows
Niektóre zasady Chrome mogą wymuszać korzystanie z Chrome lub Chromium z określonymi rozszerzeniami.
Puppeteer przekazuje domyślnie flagę --disable-extensions
, dlatego nie uruchamia się, gdy takie zasady są aktywne.
Aby obejść ten problem, spróbuj uruchomić tryb bez flagi:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});
Kontekst: problem 3681.
Chrome bez interfejsu graficznego nie uruchamia się w systemie UNIX
Zainstaluj wszystkie niezbędne zależności. Możesz uruchomić ldd chrome | grep not
na komputerze z Linuksem, aby sprawdzić brakujące zależności.
Zależności 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
Zależności 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
Po zainstalowaniu zależności musisz zaktualizować bibliotekę nss za pomocą tego polecenia
yum update nss -y
Sprawdź dyskusje:
- #290 – Rozwiązywanie problemów z Debianem
- #391 – Rozwiązywanie problemów z CentOS
- #379 – Rozwiązywanie problemów wg Alpejskich
Chrome bez interfejsu graficznego wyłącza komponowanie GPU
Chrome i Chromium wymagają --use-gl=egl
, aby umożliwić
przyspieszenie GPU w trybie bez interfejsu graficznego.
const browser = await puppeteer.launch({
headless: true,
args: ['--use-gl=egl'],
});
Przeglądarka Chrome została pobrana, ale nie uruchamia się w środowisku Node.js
Jeśli podczas próby uruchomienia Chromium widzisz błąd podobny do tego:
(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
Oznacza to, że przeglądarka została pobrana, ale nie udało się jej prawidłowo wyodrębnić.
Najczęstszą przyczyną jest błąd w Node.js w wersji 14.0.0, w którym zepsuł się extract-zip
– moduł Puppeteer służący do wyodrębniania pobranych plików przeglądarki we właściwe miejsce. Błąd został naprawiony w Node.js w wersji 14.1.0, więc upewnij się, że korzystasz z tej wersji lub nowszej.
Konfigurowanie piaskownicy w Chrome Linux
Aby chronić środowisko hosta przed niezaufanymi treściami z internetu, Chrome stosuje kilka warstw piaskownicy.
Aby to działało prawidłowo, najpierw należy skonfigurować hosta. Jeśli nie ma odpowiedniej piaskownicy do użycia w Chrome, przeglądarka ulega awarii i wyświetla błąd No usable sandbox!
.
Jeśli całkowicie ufasz treściom, które otwierasz w Chrome, możesz uruchomić Chrome, używając argumentu --no-sandbox
:
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
Piaskownicę w Chromium możesz skonfigurować na 2 sposoby.
[Zalecane] Włącz klonowanie przestrzeni nazw użytkownika
Klonowanie przestrzeni nazw sser jest obsługiwane tylko przez nowoczesne jądra. Przestrzenie nazw użytkowników bez uprawnień można zwykle włączyć, ale mogą zwiększyć ryzyko ataku jądra dla procesów innych niż root (poza piaskownicą), aby podnieść poziom uprawnień jądra.
sudo sysctl -w kernel.unprivileged_userns_clone=1
[alternative] Skonfiguruj piaskownicę identyfikatora setuid
Piaskownica setuid to samodzielny plik wykonywalny, który znajduje się obok Chromium, który jest pobierany przez Puppeteer. Ten sam plik wykonywalny piaskownicy można używać w różnych wersjach Chromium, więc te czynności można wykonać tylko raz w każdym środowisku hosta:
# 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
Warto domyślnie wyeksportować zmienną środowiskową CHROME_DEVEL_SANDBOX
. W takim przypadku do ~/.bashrc
lub .zshenv
dodaj ten kod:
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
Uruchom Puppeteer w Travis CI
Testy platformy Puppeteer prowadziliśmy w travis CI do wersji 6.0.0, a potem przenieśliśmy się na Działania GitHub. Więcej informacji znajdziesz w .travis.yml
(v5.5.0).
Poniżej przedstawiamy kilka sprawdzonych metod:
- Aby uruchamiać Chromium w trybie bez interfejsu graficznego, należy uruchomić usługę xvfb
- Domyślnie działa w systemie Xenial Linux w systemie Travis
- Domyślnie działa:
npm install
- Aplikacja
node_modules
jest domyślnie przechowywana w pamięci podręcznej
.travis.yml
może wyglądać tak:
language: node_js
node_js: node
services: xvfb
script:
- npm run test
Uruchom Puppeteer w CircleCI
- Zacznij od obrazu NodeJS w konfiguracji.
yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
- Zależności takie jak
libXtst6
prawdopodobnie wymagają instalacjiapt-get
, użyj więc obiektu threetreeslight/puppeteer (instructions) lub wklej fragmenty źródła we własnej konfiguracji. - Jeśli używasz Puppeteer przez Jest, możesz napotkać błąd podczas rozpoczynania procesów podrzędnych:
shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11)
Prawdopodobnie jest to spowodowane automatycznym wykrywaniem liczby procesów na całym komputerze (36
), a nie liczbie dozwolonych w kontenerze (2
). Aby rozwiązać ten problem, ustaw wartośćjest --maxWorkers=2
w poleceniu testowym.
Uruchamianie Puppeteer w Dockerze
Skonfigurowanie i uruchomienie bez interfejsu graficznego Chrome w Docku może być trudne. W pakiecie Chromium instalowanym przez Puppeteer brakuje wymaganych zależności od zasobów wspólnych.
Aby rozwiązać ten problem, musisz zainstalować w pliku Dockerfile brakujące zależności i najnowszy pakiet 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"]
Utwórz kontener:
docker build -t puppeteer-chrome-linux .
Uruchom kontener, przekazując node -e "<yourscript.js content as a string>"
jako polecenie:
docker run -i --init --rm --cap-add=SYS_ADMIN \
--name puppeteer-chrome puppeteer-chrome-linux \
node -e "`cat yourscript.js`"
Pełny przykład znajdziesz na https://github.com/ebidel/try-puppeteer. Dowiesz się z niego, jak uruchomić plik Dockerfile na serwerze WWW działającym w App Engine Flex (Node).
Bieganie po Alpach
Najnowszy pakiet Chromium obsługiwany w Alpine to 100, co odpowiada wersji Puppeteer 13.5.0.
Przykładowy plik 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
...
Sprawdzone metody korzystania z Dockera
Domyślnie Docker uruchamia kontener z 64 MB pamięci współdzielonej w usłudze /dev/shm
.
Jest on zwykle za mały dla Chrome i będzie powodować awarie Chrome podczas renderowania dużych stron. Aby rozwiązać ten problem, uruchom kontener za pomocą funkcji docker run --shm-size=1gb
, aby zwiększyć rozmiar elementu /dev/shm
. Od wersji Chrome 65 nie jest to już konieczne. Zamiast tego uruchom przeglądarkę z flagą --disable-dev-shm-usage
:
const browser = await puppeteer.launch({
args: ['--disable-dev-shm-usage'],
});
Spowoduje to zapisanie plików pamięci udostępnionej w folderze /tmp
zamiast /dev/shm
. Wejdź na crbug.com/736452.
Czy podczas uruchamiania Chrome widzisz inne dziwne błędy? Jeśli programujesz lokalnie, spróbuj uruchomić kontener za pomocą narzędzia docker run --cap-add=SYS_ADMIN
. Dockerfile dodaje użytkownika pptr
jako użytkownika bez uprawnień, więc może nie mieć wszystkich niezbędnych uprawnień.
Jeśli masz do czynienia z wieloma procesami zombie w Chrome, warto sprawdzić dumb-init. Procesy z dyrektywą PID=1
są objęte specjalnym traktowaniem, które w niektórych przypadkach utrudniają prawidłowe zakończenie działania Chrome (np. za pomocą Dockera).
Uruchom Puppeteer w chmurze
W Google App Engine
Środowisko wykonawcze Node.js w standardowym środowisku App Engine zawiera wszystkie pakiety systemowe potrzebne do uruchamiania Chrome bez interfejsu graficznego.
Aby użyć funkcji puppeteer
, wymień moduł jako zależność w package.json
i wdróż ją w Google App Engine. Więcej informacji o korzystaniu z puppeteer
w App Engine znajdziesz w oficjalnym samouczku.
W Google Cloud Functions
Środowisko wykonawcze Node.js 10 Google Cloud Functions zawiera wszystkie pakiety systemowe potrzebne do uruchamiania Chrome bez interfejsu graficznego.
Aby użyć funkcji puppeteer
, wymień ten moduł jako zależność w package.json
i wdróż swoją funkcję w Google Cloud Functions za pomocą środowiska wykonawczego nodejs10
.
Uruchom Puppeteer w Google Cloud Run
Domyślne środowisko wykonawcze Node.js Google Cloud Run nie zawiera pakietów systemowych niezbędnych do uruchamiania Chrome bez interfejsu graficznego. Skonfiguruj własny Dockerfile
i uwzględnij brakujące zależności.
W Heroku
Uruchomienie Puppeteer na Heroku wymaga dodatkowych zależności, których nie znajdziesz w pudełku z systemem Linux, który tworzy dla Ciebie Heroku. Aby dodać zależności dotyczące wdrożenia, dodaj pakiet kompilacji Puppeteer Heroku do listy pakietów kompilacji dla aplikacji w sekcji Ustawienia > Pakiety kompilacji.
Adres URL pakietu kompilacji to https://github.com/jontewks/puppeteer-heroku-buildpack
Upewnij się, że podczas uruchamiania Puppeteer używasz trybu '--no-sandbox'
. Aby to zrobić, przekaż go jako argument do wywołania .launch()
: puppeteer.launch({ args: ['--no-sandbox'] });
.
Gdy klikniesz Dodaj pakiet kompilacji, wklej ten adres URL w polu wejściowym i kliknij Zapisz. Podczas następnego wdrożenia aplikacja zainstaluje również zależności, które musi uruchomić Puppeteer.
Jeśli chcesz renderować znaki chińskie, japońskie lub koreańskie, możesz użyć pakietu kompilacji z dodatkowymi plikami czcionek, takimi jak https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
Dostępny jest też inny przewodnik z @timleland, w którym znajdziesz przykładowy projekt.
W AWS Lambda
AWS Lambda limituje rozmiar pakietów wdrożeniowych do około 50 MB. Stanowi to wyzwanie dla uruchamiania bez interfejsu graficznego Chrome (a tym samym Puppeteer) na Lambda. Społeczność zebrała kilka zasobów, które pomagają rozwiązać te problemy:
- Plik binarny Chromium dla AWS Lambda i Google Cloud Functions (zaktualizowany o najnowszą stabilną wersję Puppeteer)
- Chrome/Chromium na AWS Lambda od Marco Lüthy'ego to wtyczka bezserwerowa i nieaktualna.
Instancja AWS EC2 z systemem Amazon-Linux
Jeśli masz instancję EC2 z amazon-linux w swoim potoku CI/CD i chcesz uruchomić testy Puppeteer w amazon-linux, wykonaj te czynności.
Aby zainstalować Chromium, musisz najpierw włączyć
amazon-linux-extras
, który jest częścią EPEL (Extra Packages for Enterprise Linux):sudo amazon-linux-extras install epel -y
Następnie zainstaluj Chromium:
sudo yum install -y chromium
Teraz Puppeteer może uruchomić Chromium i przeprowadzić testy. Jeśli nie włączysz EPEL i nadal będziesz instalować Chromium w ramach npm install
, Puppeteer nie będzie mógł uruchomić Chromium z powodu niedostępności libatk-1.0.so.0
i wielu innych pakietów.
Problemy z transpilacją kodu
Jeśli korzystasz z transpilera JavaScriptu takiego jak Babel czy TypeScript, wywołanie evaluate()
z funkcją asynchroniczną może nie działać. Dzieje się tak, ponieważ chociaż puppeteer
używa Function.prototype.toString()
do serializacji funkcji, a transpilatory mogą zmieniać kod wyjściowy w taki sposób, że jest on niezgodny z puppeteer
.
Rozwiązaniem tego problemu jest poinstruowanie transpilera, aby nie zakłócił kodu. Na przykład skonfiguruj TypeScript do korzystania z najnowszej wersji ecma ("target": "es2018"
). Innym obejściem problemu może być użycie szablonów ciągów tekstowych zamiast funkcji:
await page.evaluate(`(async() => {
console.log('1');
})()`);