Puppeteer ile ilgili sorunları giderme

Gözetimsiz Chrome, Windows'da başlatılmıyor

Bazı Chrome politikaları, Chrome'un veya Chromium'un belirli Uzantılarla çalışmasını zorunlu kılabilir.

Puppeteer varsayılan olarak --disable-extensions işaretini iletir ve bu nedenle, bu tür politikalar etkin olduğunda başlatılamaz.

Bu sorunu çözmek için bayrak olmadan koşmayı deneyin:

const browser = await puppeteer.launch({
  ignoreDefaultArgs: ['--disable-extensions'],
});

İçerik: issue 3681.

Gözetimsiz Chrome UNIX'te başlatılmıyor

Gerekli tüm bağımlılıkların yüklendiğinden emin olun. Hangi bağımlılıkların eksik olduğunu kontrol etmek için ldd chrome | grep not uygulamasını bir Linux makinesinde çalıştırabilirsiniz.

Debian (Ubuntu) bağımlılıkları

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 Bağımlılıkları

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

Bağımlılıkları yükledikten sonra bu komutu kullanarak nss kitaplığını güncellemeniz gerekir

yum update nss -y

Tartışmalara göz atın:

  • #290 - Debian sorun giderme
  • #391 - CentOS ile ilgili sorunları giderme
  • #379 - Alpler'de sorun giderme

Gözetimsiz Chrome, GPU birleştirmeyi devre dışı bırakır

Chrome ve Chromium, gözetimsiz modda GPU hızlandırmayı etkinleştirmek için --use-gl=egl eklentisini gerektirir.

const browser = await puppeteer.launch({
  headless: true,
  args: ['--use-gl=egl'],
});

Chrome indirildi ancak Node.js'de başlatılamıyor

Chromium'u başlatmaya çalışırken aşağıdakine benzer bir hata alırsanız:

(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

Bu, tarayıcının indirildiği ancak düzgün şekilde çıkartılamadığı anlamına gelir. Bunun en yaygın nedeni Node.js v14.0.0'da extract-zip bozan bir hatadır. Puppeteer modülünün tarayıcı indirmelerini doğru yere çıkarmak için kullandığı bir modül. Hata, Node.js v14.1.0'da düzeltildi. Bu nedenle, söz konusu sürümü veya daha üstünü kullandığınızdan emin olun.

Chrome Linux korumalı alanı oluşturma

Chrome, ana makine ortamını güvenilir olmayan web içeriğinden korumak için birden fazla korumalı alan katmanı kullanır. Bunun düzgün bir şekilde çalışması için öncelikle ana makinenin yapılandırılması gerekir. Chrome'un kullanabileceği iyi bir korumalı alan yoksa No usable sandbox! hatasıyla kilitlenir.

Chrome'da açtığınız içeriğe kesinlikle güveniyorsanız Chrome'u --no-sandbox bağımsız değişkeniyle başlatabilirsiniz:

const browser = await puppeteer.launch({
  args: ['--no-sandbox', '--disable-setuid-sandbox'],
});

Chromium'da korumalı alanı yapılandırmanın 2 yolu vardır.

Sser ad alanı klonlama yalnızca modern çekirdekler tarafından desteklenir. Ayrıcalığa sahip olmayan kullanıcı ad alanları genellikle etkinleştirilebilir ancak çekirdek ayrıcalıklarını yükseltmek amacıyla kök olmayan (korumalı olmayan) işlemler için daha fazla çekirdek saldırı yüzeyi açabilir.

sudo sysctl -w kernel.unprivileged_userns_clone=1

[alternatif] Setuid korumalı alanı kurulumu

setuid korumalı alanı bağımsız bir yürütülebilir dosya olarak gelir ve Kuklacı'nın indirdiği Chromium'un yanında bulunur. Aynı korumalı korumalı alanı farklı Chromium sürümleri için yeniden kullanabilirsiniz. Bu nedenle aşağıdaki işlemler ana makine ortamı başına yalnızca bir kez yapılabilir:

# 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

Varsayılan olarak CHROME_DEVEL_SANDBOX env değişkenini dışa aktarmak isteyebilirsiniz. Bu durumda, aşağıdaki kodu ~/.bashrc veya .zshenv öğesine ekleyin:

export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox

Travis CI'da Puppeteer'ı Çalıştır

Puppeteer için Travis CI'da 6.0.0 sürümüne kadar testler yaptık ve sonrasında GitHub Actions'a geçiş yaptık. Referans için .travis.yml (v5.5.0) sayfasını inceleyebilirsiniz.

Aşağıda bazı en iyi uygulamaları görebilirsiniz:

  • Chromium'u gözetimsiz modda çalıştırmak için xvfb hizmeti başlatılmalıdır
  • Varsayılan olarak Travis üzerindeki Xenial Linux'ta çalışır
  • Varsayılan olarak npm install çalışır
  • node_modules varsayılan olarak önbelleğe alınır

.travis.yml şöyle görünebilir:

language: node_js
node_js: node
services: xvfb

script:
  - npm run test

CircleCI'da Puppeteer'ı Çalıştır

  1. Yapılandırmanıza bir NodeJS görüntüsü ekleyerek başlayın. yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
  2. libXtst6 gibi bağımlılıkların muhtemelen apt-get ile yüklenmesi gerekir. Bu nedenle threetreeslight/puppeteer orb'u (instructions) kullanın veya kaynağının parçalarını kendi yapılandırmanıza yapıştırın.
  3. Son olarak, Jest üzerinden Puppeteer kullanıyorsanız alt işlemlerde hata oluşmasıyla karşılaşabilirsiniz: shell [00:00.0] jest args: --e2e --spec --max-workers=36 Error: spawn ENOMEM at ChildProcess.spawn (internal/child_process.js:394:11) Bu durum büyük olasılıkla Jest'in container'ınıza (2) izin verilen sayı yerine makinenin tamamındaki (36) işlem sayısını otomatik olarak algılamasından kaynaklanmaktadır. Bunu düzeltmek için test komutunuzda jest --maxWorkers=2 değerini ayarlayın.

Docker'da Puppeteer'ı Çalıştır

Gözetimsiz Chrome'u Docker'da çalışır duruma getirmek zor olabilir. Puppeteer'ın yüklediği paket halinde sunulan Chromium'da gerekli paylaşılan kitaplık bağımlılıkları yok.

Düzeltmek için eksik bağımlılıkları ve en son Chromium paketini Dockerfile dosyanıza yüklemeniz gerekir:

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"]

Kapsayıcıyı oluşturun:

docker build -t puppeteer-chrome-linux .

node -e "<yourscript.js content as a string>" komutunu komut olarak geçirerek kapsayıcıyı çalıştırın:

 docker run -i --init --rm --cap-add=SYS_ADMIN \
   --name puppeteer-chrome puppeteer-chrome-linux \
   node -e "`cat yourscript.js`"

Bu Dockerfile'ın App Engine Flex (Node) üzerinde çalışan bir web sunucusundan nasıl çalıştırılacağını gösteren tam bir örneği https://github.com/ebidel/try-puppeteer adresinde bulabilirsiniz.

Alpler'de koş

Alpine'de desteklenen en yeni Chromium paketi 100'dür ve bu paket, Puppeteer v13.5.0'a karşılık gelir.

Örnek 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 ile ilgili en iyi uygulamalar

Varsayılan olarak Docker, 64 MB boyutunda /dev/shm paylaşılan bellek alanına sahip bir container çalıştırır. Bu boyut, Chrome için genellikle çok küçük olup büyük sayfalar oluşturulurken Chrome'un kilitlenmesine neden olur. Bu sorunu düzeltmek için kapsayıcıyı docker run --shm-size=1gb ile çalıştırarak /dev/shm boyutunu artırın. Chrome 65'ten itibaren buna gerek yoktur. Bunun yerine, tarayıcıyı --disable-dev-shm-usage işaretiyle başlatın:

const browser = await puppeteer.launch({
  args: ['--disable-dev-shm-usage'],
});

Bu işlem, paylaşılan anı dosyalarını /dev/shm yerine /tmp dosyasına yazar. crbug.com/736452 adresini inceleyin.

Chrome'u başlatırken başka tuhaf hatalarla karşılaşıyor musunuz? Yerel olarak geliştirme yaparken container'ınızı docker run --cap-add=SYS_ADMIN ile çalıştırmayı deneyin. Dockerfile bir pptr kullanıcısını ayrıcalıklı olmayan kullanıcı olarak eklediğinden gerekli tüm ayrıcalıklara sahip olmayabilir.

dumb-init ifadesi, sürekli olarak çok sayıda zombi ile karşılaşıp karşılaşmadığınızı kontrol etmenizi sağlar. PID=1 ile yapılan işlemlerde özel işlemler vardır. Bu da bazı durumlarda (örneğin Docker'da) Chrome'un doğru şekilde sonlandırılmasını zorlaştırır.

Puppeteer'ı bulutta çalıştırın

Google App Engine'de

App Engine standart ortamının Node.js çalışma zamanı, gözetimsiz Chrome'u çalıştırmak için gereken tüm sistem paketleriyle birlikte gelir.

puppeteer kullanmak için modülü package.json dosyanızda bağımlılık olarak listeleyin ve Google App Engine'e dağıtın. Resmi eğiticiyi izleyerek puppeteer App Engine'de kullanma hakkında daha fazla bilgi edinin.

Google Cloud Functions'da

Google Cloud Functions'ın Node.js 10 çalışma zamanı, gözetimsiz Chrome'u çalıştırmak için gereken tüm sistem paketleriyle birlikte gelir.

puppeteer kullanmak için modülü package.json dosyanızda bağımlılık olarak listeleyin ve nodejs10 çalışma zamanını kullanarak işlevinizi Google Cloud Functions'a dağıtın.

Google Cloud Run'da Puppeteer'ı Çalıştır

Google Cloud Run'ın varsayılan Node.js çalışma zamanı, gözetimsiz Chrome'u çalıştırmak için gereken sistem paketleriyle birlikte verilmez. Kendi Dockerfile öğenizi kurun ve eksik bağımlılıkları ekleyin.

Heroku'da

Heroku'da Puppeteer'ı çalıştırmak, Heroku'nun sizin için hazırladığı Linux kutusunda bulunmayan bazı ek bağımlılıklar gerektirir. Dağıtım sırasında bağımlılıkları eklemek için Puppeteer Heroku derleme paketini, Ayarlar > Derleme Paketleri altında uygulamanızın derleme paketleri listesine ekleyin.

Derleme paketinin URL'si: https://github.com/jontewks/puppeteer-heroku-buildpack

Puppeteer'ı başlatırken '--no-sandbox' modunu kullandığınızdan emin olun. Bu işlem, .launch() çağrınıza bağımsız değişken olarak iletilerek yapılabilir: puppeteer.launch({ args: ['--no-sandbox'] });.

Buildpack'i ekle'yi tıkladığınızda URL'yi girişe yapıştırın ve Kaydet'i tıklayın. Bir sonraki dağıtımda uygulamanız, Puppeteer'ın çalıştırması gereken bağımlılıkları da yükler.

Çince, Japonca veya Korece karakterler oluşturmanız gerekiyorsa https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack gibi ek yazı tipi dosyaları içeren bir derleme paketi kullanmanız gerekebilir

Ayrıca örnek bir proje içeren başka bir @timleland rehberi daha var.

AWS Lambda'da

AWS Lambda, dağıtım paketi boyutlarını yaklaşık 50 MB ile sınırlar. Bu durum, Chrome'u (ve dolayısıyla Puppeteer'ı) Lambda'da gözetimsiz çalıştırmanın zorluğunu da beraberinde getirir. Topluluk, bu sorunlara çözüm bulmak için birkaç kaynak bir araya getirdi:

Amazon-Linux çalıştıran AWS EC2 örneği

CI/CD ardışık düzeninizde amazon-linux çalıştıran bir EC2 örneğiniz varsa ve amazon-linux'da Puppeteer testlerini çalıştırmak istiyorsanız aşağıdaki adımları uygulayın.

  1. Chromium'u yüklemek için önce EPEL (Enterprise Linux İçin Ek Paketler) kapsamında olan amazon-linux-extras özelliğini etkinleştirmeniz gerekir:

    sudo amazon-linux-extras install epel -y
    
  2. Ardından Chromium'u yükleyin:

    sudo yum install -y chromium
    

Puppeteer artık testlerinizi çalıştırmak için Chromium'u başlatabilir. EPEL'i etkinleştirmez ve Chromium'u npm install kapsamında yüklemeye devam ederseniz Puppeteer, libatk-1.0.so.0 ve daha birçok paketin kullanılamaması nedeniyle Chromium'u başlatamaz.

Kod çevirme sorunları

Babe veya TypeScript gibi bir JavaScript aktarıcı kullanıyorsanız eşzamansız işlevle evaluate() çağrısı yapılamayabilir. Çünkü puppeteer, işlevleri serileştirmek için Function.prototype.toString() kullanırken aktarıcılar çıkış kodunu puppeteer ile uyumsuz bir şekilde değiştirebilir.

Bu sorunu çözmek için uygulanabilecek bazı geçici çözümler, aktarıcıya kodu karıştırmamasını söylemek olabilir. Örneğin, TypeScript'i en son ecma sürümünü ("target": "es2018") kullanacak şekilde yapılandırabilirsiniz.

await page.evaluate(`(async() => {
   console.log('1');
})()`);