Headless Chrome start niet op Windows
Sommige Chrome-beleidsregels kunnen het uitvoeren van Chrome of Chromium met bepaalde extensies afdwingen.
Puppeteer passeert standaard de vlag --disable-extensions
en start daarom niet wanneer dergelijk beleid actief is.
Om dit te omzeilen, probeer zonder de vlag te rennen:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--disable-extensions'],
});
Context: uitgave 3681 .
Headless Chrome start niet op UNIX
Zorg ervoor dat alle benodigde afhankelijkheden zijn geïnstalleerd. U kunt ldd chrome | grep not
op een Linux-machine om te controleren welke afhankelijkheden ontbreken.
Afhankelijkheden van 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-afhankelijkheden
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
Na het installeren van afhankelijkheden moet u de nss-bibliotheek bijwerken met deze opdracht
yum update nss -y
Bekijk discussies:
- #290 - Probleemoplossing voor Debian
- #391 - Probleemoplossing CentOS
- #379 - Alpine-probleemoplossing
Headless Chrome schakelt GPU-compositing uit
Chrome en Chromium vereisen --use-gl=egl
om GPU-versnelling in headless-modus in te schakelen.
const browser = await puppeteer.launch({
headless: true,
args: ['--use-gl=egl'],
});
Chrome wordt gedownload, maar kan niet worden gestart op Node.js
Als je een foutmelding krijgt die er als volgt uitziet wanneer je Chromium probeert te starten:
(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
Dit betekent dat de browser is gedownload, maar niet correct is uitgepakt. De meest voorkomende oorzaak is een bug in Node.js v14.0.0 waardoor extract-zip
kapot ging, de module die Puppeteer gebruikt om browserdownloads naar de juiste plek te extraheren. De bug is opgelost in Node.js v14.1.0, dus zorg ervoor dat je die versie of hoger gebruikt.
Stel een Chrome Linux-sandbox in
Om de hostomgeving te beschermen tegen niet-vertrouwde webinhoud, gebruikt Chrome meerdere lagen sandboxing . Om dit goed te laten werken, moet de host eerst worden geconfigureerd. Als er geen goede sandbox is die Chrome kan gebruiken, crasht het programma met de foutmelding No usable sandbox!
.
Als u de inhoud die u in Chrome opent absoluut vertrouwt , kunt u Chrome starten met het argument --no-sandbox
:
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
Er zijn twee manieren om een sandbox in Chromium te configureren.
[aanbevolen] Schakel het klonen van de gebruikersnaamruimte in
Het klonen van Sser-naamruimten wordt alleen ondersteund door moderne kernels. Gebruikersnaamruimten zonder privileges zijn over het algemeen prima in te schakelen, maar kunnen meer kernelaanvaloppervlak openen voor (niet in de sandbox geplaatste) niet-rootprocessen, zodat ze kernelprivileges kunnen krijgen.
sudo sysctl -w kernel.unprivileged_userns_clone=1
[alternatief] Setuid-sandbox instellen
De setuid-sandbox wordt geleverd als een zelfstandig uitvoerbaar bestand en bevindt zich naast het Chromium dat Puppeteer downloadt. Het is prima om hetzelfde uitvoerbare sandbox-bestand opnieuw te gebruiken voor verschillende Chromium-versies, dus het volgende kan slechts één keer per hostomgeving worden gedaan:
# 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
Mogelijk wilt u de CHROME_DEVEL_SANDBOX
env-variabele standaard exporteren. Voeg in dit geval het volgende toe aan ~/.bashrc
of .zshenv
:
export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
Voer Puppeteer uit op Travis CI
We hebben onze tests voor Puppeteer op Travis CI uitgevoerd tot v6.0.0, waarna we zijn gemigreerd naar GitHub Actions. U kunt .travis.yml
(v5.5.0) ter referentie raadplegen.
Hier zijn enkele best practices:
- De xvfb -service moet worden gestart om Chromium in de niet-headless-modus uit te voeren
- Draait standaard op Xenial Linux op Travis
- Voert standaard
npm install
uit -
node_modules
wordt standaard in de cache opgeslagen
.travis.yml
zou er zo uit kunnen zien:
language: node_js
node_js: node
services: xvfb
script:
- npm run test
Voer Puppeteer uit op CircleCI
- Begin met een NodeJS-image in uw configuratie.
yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
- Afhankelijkheden zoals
libXtst6
moeten waarschijnlijk worden geïnstalleerd metapt-get
, dus gebruik de threetreeslight/puppeteer orb ( instructies ), of plak delen van de broncode in uw eigen configuratie. - Als u ten slotte Puppeteer via Jest gebruikt, kunt u een fout tegenkomen bij het voortbrengen van onderliggende processen:
shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11)
Dit wordt waarschijnlijk veroorzaakt doordat Jest automatisch het aantal processen op de hele machine detecteert (36
) in plaats van het aantal dat is toegestaan voor uw container (2
). Om dit op te lossen, stelt ujest --maxWorkers=2
in uw testopdracht in.
Voer Puppeteer uit in Docker
Het kan lastig zijn om headless Chrome in Docker te gebruiken. Het gebundelde Chromium dat Puppeteer installeert, mist de noodzakelijke afhankelijkheden van de gedeelde bibliotheek.
Om dit probleem op te lossen, moet je de ontbrekende afhankelijkheden en het nieuwste Chromium-pakket in je Dockerfile installeren:
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"]
Bouw de container:
docker build -t puppeteer-chrome-linux .
Voer de container uit door node -e "<yourscript.js content as a string>"
als opdracht:
docker run -i --init --rm --cap-add=SYS_ADMIN \
--name puppeteer-chrome puppeteer-chrome-linux \
node -e "`cat yourscript.js`"
Er is een volledig voorbeeld op https://github.com/ebidel/try-puppeteer dat laat zien hoe u dit Dockerfile kunt uitvoeren vanaf een webserver die draait op App Engine Flex (Node).
Rijd op Alpine
Het nieuwste Chromium-pakket dat op Alpine wordt ondersteund, is 100, wat overeenkomt met Puppeteer v13.5.0 .
Voorbeeld Dockerbestand:
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
...
Best practices met Docker
Standaard voert Docker een container uit met een /dev/shm
gedeelde geheugenruimte van 64 MB. Dit is doorgaans te klein voor Chrome en zorgt ervoor dat Chrome crasht bij het weergeven van grote pagina's. Om dit op te lossen, voert u de container uit met docker run --shm-size=1gb
om de grootte van /dev/shm
te vergroten. Sinds Chrome 65 is dit niet meer nodig. Start in plaats daarvan de browser met de vlag --disable-dev-shm-usage
:
const browser = await puppeteer.launch({
args: ['--disable-dev-shm-usage'],
});
Dit schrijft gedeelde geheugenbestanden naar /tmp
in plaats van /dev/shm
. Beoordeel crbug.com/736452 .
Zie je andere rare fouten bij het starten van Chrome? Probeer uw container uit te voeren met docker run --cap-add=SYS_ADMIN
wanneer u lokaal ontwikkelt. Omdat de Dockerfile een pptr
gebruiker toevoegt als een niet-bevoorrechte gebruiker, beschikt deze mogelijk niet over alle benodigde rechten.
dumb-init is de moeite van het bekijken waard als je veel zombie-Chrome-processen ervaart die blijven hangen. Er is een speciale behandeling voor processen met PID=1
, waardoor het in sommige gevallen moeilijk wordt om Chrome op de juiste manier te beëindigen (zoals bij Docker).
Voer Puppeteer uit in de cloud
Op Google App Engine
De Node.js-runtime van de App Engine-standaardomgeving wordt geleverd met alle systeempakketten die nodig zijn om Headless Chrome uit te voeren.
Als puppeteer
wilt gebruiken, vermeldt u de module als afhankelijkheid in uw package.json
en implementeert u deze in Google App Engine. Lees meer over het gebruik van puppeteer
op App Engine door de officiële tutorial te volgen.
Op Google Cloud-functies
De Node.js 10-runtime van Google Cloud Functions wordt geleverd met alle systeempakketten die nodig zijn om Headless Chrome uit te voeren.
Als u puppeteer
wilt gebruiken, vermeldt u de module als afhankelijkheid in uw package.json
en implementeert u uw functie in Google Cloud Functions met behulp van de nodejs10
-runtime.
Voer Puppeteer uit op Google Cloud Run
De standaard Node.js-runtime van Google Cloud Run wordt niet geleverd met de systeempakketten die nodig zijn om Headless Chrome uit te voeren. Stel uw eigen Dockerfile
in en neem de ontbrekende afhankelijkheden op .
Op Heroku
Het uitvoeren van Puppeteer op Heroku vereist een aantal extra afhankelijkheden die niet zijn opgenomen in de Linux-box die Heroku voor je opstart. Als u de afhankelijkheden bij de implementatie wilt toevoegen, voegt u het Puppeteer Heroku-buildpack toe aan de lijst met buildpacks voor uw app onder Instellingen > Buildpacks.
De URL voor het buildpack is https://github.com/jontewks/puppeteer-heroku-buildpack
Zorg ervoor dat u de '--no-sandbox'
modus gebruikt wanneer u Puppeteer start. Dit kunt u doen door het als argument door te geven aan uw .launch()
-aanroep: puppeteer.launch({ args: ['--no-sandbox'] });
.
Wanneer u op buildpack toevoegen klikt, plakt u die URL in de invoer en klikt u op opslaan . Bij de volgende implementatie installeert uw app ook de afhankelijkheden die Puppeteer moet uitvoeren.
Als u Chinese, Japanse of Koreaanse tekens moet weergeven, moet u mogelijk een buildpack gebruiken met extra lettertypebestanden zoals https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
Er is ook nog een gids van @timleland met een voorbeeldproject.
Op AWS Lambda
AWS Lambda beperkt de grootte van implementatiepakketten tot ~50 MB. Dit brengt uitdagingen met zich mee voor het uitvoeren van headless Chrome (en dus Puppeteer) op Lambda. De community heeft een aantal bronnen samengesteld die deze problemen aanpakken:
- Chromium Binary voor AWS Lambda en Google Cloud Functions (bijgewerkt gehouden met de nieuwste stabiele release van Puppeteer)
- Chrome/Chromium op AWS Lambda van Marco Lüthy is een serverloze plug-in en is verouderd.
AWS EC2-instantie met Amazon-Linux
Als u een EC2-instantie met amazon-linux in uw CI/CD-pijplijn hebt en u Puppeteer-tests wilt uitvoeren in amazon-linux, volgt u deze stappen.
Om Chromium te installeren, moet je eerst
amazon-linux-extras
inschakelen, wat onderdeel is van EPEL (Extra Packages for Enterprise Linux) :sudo amazon-linux-extras install epel -y
Installeer vervolgens Chromium:
sudo yum install -y chromium
Nu kan Puppeteer Chromium starten om uw tests uit te voeren. Als u EPEL niet inschakelt en doorgaat met het installeren van Chromium als onderdeel van npm install
, kan Puppeteer Chromium niet starten vanwege de onbeschikbaarheid van libatk-1.0.so.0
en nog veel meer pakketten.
Problemen met codetranspilatie
Als u een JavaScript-transpiler zoals babel of TypeScript gebruikt, werkt het aanroepen van evaluate()
met een asynchrone functie mogelijk niet. Dit komt doordat puppeteer
Function.prototype.toString()
gebruikt om functies te serialiseren, terwijl transpilers de uitvoercode zodanig kunnen wijzigen dat deze incompatibel is met puppeteer
.
Sommige oplossingen voor dit probleem zijn het instrueren van de transpiler om de code niet te verknoeien, bijvoorbeeld door TypeScript te configureren om de nieuwste ecma-versie te gebruiken ( "target": "es2018"
). Een andere oplossing zou het gebruik van tekenreekssjablonen kunnen zijn in plaats van functies:
await page.evaluate(`(async() => {
console.log('1');
})()`);