عیب یابی Puppeteer

هدلس کروم در ویندوز راه اندازی نمی شود

برخی از خط‌مشی‌های Chrome ممکن است اجرای Chrome یا Chromium را با برنامه‌های افزودنی خاص اعمال کنند.

Puppeteer به طور پیش‌فرض پرچم --disable-extensions را پاس می‌کند و بنابراین وقتی چنین خط‌مشی‌هایی فعال هستند راه‌اندازی نمی‌شود.

برای حل این مشکل، بدون پرچم اجرا کنید:

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

متن: شماره 3681 .

هدلس کروم در یونیکس راه اندازی نمی شود

مطمئن شوید که تمام وابستگی های لازم نصب شده اند. می توانید ldd chrome | grep not تا بررسی کند کدام وابستگی وجود ندارد.

وابستگی های دبیان (اوبونتو).

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

بررسی بحث ها:

  • #290 - عیب یابی دبیان
  • #391 - عیب یابی CentOS
  • #379 - عیب یابی آلپاین

Headless Chrome ترکیب GPU را غیرفعال می کند

Chrome و Chromium برای فعال کردن شتاب GPU در حالت بدون سر به --use-gl=egl نیاز دارند.

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 v14.1.0 برطرف شد، بنابراین مطمئن شوید که آن نسخه یا بالاتر را اجرا می کنید.

یک جعبه ایمنی Chrome Linux راه اندازی کنید

برای محافظت از محیط میزبان در برابر محتوای وب غیرقابل اعتماد، Chrome از چندین لایه sandboxing استفاده می‌کند. برای اینکه این کار به درستی کار کند، ابتدا میزبان باید پیکربندی شود. اگر سندباکس خوبی برای استفاده کروم وجود نداشته باشد، با خطای No usable sandbox! .

اگر به محتوایی که در Chrome باز می کنید کاملاً اعتماد دارید، می توانید Chrome را با آرگومان --no-sandbox راه اندازی کنید:

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

2 راه برای پیکربندی جعبه ایمنی در Chromium وجود دارد.

شبیه سازی فضای نام 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

ممکن است بخواهید متغیر CHROME_DEVEL_SANDBOX env را به طور پیش فرض صادر کنید. در این مورد، موارد زیر را به ~/.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 (v5.5.0) را ببینید.

در اینجا برخی از بهترین شیوه ها وجود دارد:

  • سرویس xvfb باید برای اجرای Chromium در حالت غیر هدلس راه اندازی شود
  • به طور پیش فرض روی لینوکس Xenial در تراویس اجرا می شود
  • npm install به صورت پیش فرض اجرا می کند
  • node_modules به طور پیش فرض ذخیره می شود

.travis.yml ممکن است به شکل زیر باشد:

language: node_js
node_js: node
services: xvfb

script:
  - npm run test

Puppeteer را در CircleCI اجرا کنید

  1. با یک تصویر NodeJS در پیکربندی خود شروع کنید. yaml docker: - image: circleci/node:14 # Use your desired version environment: NODE_ENV: development # Only needed if puppeteer is in `devDependencies`
  2. وابستگی هایی مانند libXtst6 احتمالاً باید با apt-get نصب شوند، بنابراین از گوی threetreeslight/puppeteer ( دستورالعمل ها ) استفاده کنید یا بخش هایی از منبع آن را در پیکربندی خود بچسبانید.
  3. در نهایت، اگر از 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) این احتمالاً به دلیل شناسایی خودکار تعداد پردازش‌ها در کل دستگاه ( 36 ) توسط Jest به جای تعداد مجاز به ظرف شما ( 2 ) است. برای رفع این مشکل، jest --maxWorkers=2 در دستور تست خود تنظیم کنید.

Puppeteer را در Docker اجرا کنید

راه‌اندازی و اجرای هدلس Chrome در 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 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 مگابایتی اجرا می‌کند. این معمولاً برای Chrome بسیار کوچک است و باعث می‌شود کروم هنگام رندر کردن صفحات بزرگ از کار بیفتد. برای رفع مشکل، کانتینر را با docker run --shm-size=1gb اجرا کنید تا اندازه /dev/shm را افزایش دهید. از Chrome 65، این دیگر ضروری نیست. در عوض، مرورگر را با پرچم --disable-dev-shm-usage راه اندازی کنید:

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

این فایل های حافظه مشترک را به جای /dev/shm در /tmp می نویسد. بررسی crbug.com/736452 .

آیا هنگام راه اندازی Chrome خطاهای عجیب دیگری می بینید؟ هنگام توسعه محلی، کانتینر خود را با docker run --cap-add=SYS_ADMIN اجرا کنید. از آنجایی که Dockerfile یک کاربر pptr را به عنوان یک کاربر غیرمجاز اضافه می کند، ممکن است تمام امتیازات لازم را نداشته باشد.

dumb-init ارزش این را دارد که بررسی کنید اگر زامبی های زیادی را تجربه می کنید که فرآیندهای کروم در اطراف شما باقی مانده است. روش خاصی برای فرآیندهای با PID=1 وجود دارد که در برخی موارد (مانند Docker) به‌درستی پایان دادن به Chrome را دشوار می‌کند.

Puppeteer را در ابر اجرا کنید

در موتور برنامه گوگل

زمان اجرا Node.js محیط استاندارد App Engine با تمام بسته های سیستمی مورد نیاز برای اجرای Headless Chrome ارائه می شود.

برای استفاده از puppeteer ، ماژول را به عنوان یک وابستگی در package.json خود فهرست کنید و در Google App Engine قرار دهید. با دنبال کردن آموزش رسمی، درباره استفاده از puppeteer در App Engine بیشتر بخوانید.

در Google Cloud Functions

زمان اجرا Node.js 10 Google Cloud Functions با تمام بسته های سیستمی مورد نیاز برای اجرای Headless Chrome ارائه می شود.

برای استفاده از puppeteer ، ماژول را به عنوان یک وابستگی در package.json خود فهرست کنید و تابع خود را با استفاده از زمان اجرا nodejs10 در Google Cloud Functions پیاده کنید.

Puppeteer را در Google Cloud Run اجرا کنید

زمان اجرای پیش‌فرض Node.js Google Cloud Run با بسته‌های سیستمی مورد نیاز برای اجرای Headless Chrome ارائه نمی‌شود. Dockerfile خود را راه اندازی کنید و وابستگی های از دست رفته را اضافه کنید .

روی هیروکو

اجرای Puppeteer در Heroku به برخی وابستگی‌های اضافی نیاز دارد که در جعبه لینوکس که Heroku برای شما می‌چرخاند وجود ندارد. برای افزودن وابستگی‌ها به هنگام استقرار، بسته ساخت Puppeteer Heroku را به لیست بسته‌های ساخت برنامه خود در تنظیمات > Buildpacks اضافه کنید.

نشانی اینترنتی buildpack https://github.com/jontewks/puppeteer-heroku-buildpack است.

هنگام راه‌اندازی Puppeteer، مطمئن شوید که از حالت '--no-sandbox' استفاده می‌کنید. این کار را می توان با ارسال آن به عنوان آرگومان به .launch() فراخوانی انجام داد: puppeteer.launch({ args: ['--no-sandbox'] }); .

وقتی روی افزودن buildpack کلیک می‌کنید، آن URL را در ورودی قرار دهید و روی ذخیره کلیک کنید. در استقرار بعدی، برنامه شما وابستگی هایی را که Puppeteer برای اجرا نیاز دارد نیز نصب می کند.

اگر نیاز به رندر کردن نویسه‌های چینی، ژاپنی یا کره‌ای دارید، ممکن است لازم باشد از یک buildpack با فایل‌های فونت اضافی مانند https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack استفاده کنید.

همچنین راهنمای دیگری از @timleland وجود دارد که شامل یک پروژه نمونه است.

در AWS Lambda

AWS Lambda اندازه بسته استقرار را به 50 مگابایت محدود می کند . این چالش‌هایی را برای اجرای کروم بدون هد (و در نتیجه Puppeteer) در Lambda ایجاد می‌کند. جامعه چند منبع را گردآوری کرده است که در مورد مسائل کار می کنند:

نمونه AWS EC2 که آمازون-لینوکس را اجرا می کند

اگر یک نمونه EC2 دارید که amazon-linux را در خط لوله CI/CD خود اجرا می کند و می خواهید تست های Puppeteer را در amazon-linux اجرا کنید، این مراحل را دنبال کنید.

  1. برای نصب Chromium، ابتدا باید amazon-linux-extras که بخشی از EPEL (بسته‌های اضافی برای لینوکس سازمانی) است، فعال کنید:

    sudo amazon-linux-extras install epel -y
    
  2. سپس، Chromium را نصب کنید:

    sudo yum install -y chromium
    

اکنون Puppeteer می‌تواند Chromium را برای اجرای آزمایش‌های شما راه‌اندازی کند. اگر EPEL را فعال نکنید و به نصب Chromium به عنوان بخشی از npm install ادامه دهید، Puppeteer نمی‌تواند Chromium را راه‌اندازی کند به دلیل در دسترس نبودن libatk-1.0.so.0 و بسیاری از بسته‌های دیگر.

مشکلات ترجمه کد

اگر از ترانسپایلر جاوا اسکریپت مانند babel یا TypeScript استفاده می کنید، evaluate() value با یک تابع async ممکن است کار نکند. این به این دلیل است که در حالی که puppeteer از Function.prototype.toString() برای سریال کردن توابع استفاده می کند در حالی که transpiler ها می توانند کد خروجی را به گونه ای تغییر دهند که با puppeteer ناسازگار باشد.

برخی از راه‌حل‌های حل این مشکل این است که به ترانسپایلر دستور دهید تا کد را به هم نریزد، برای مثال، TypeScript را برای استفاده از آخرین نسخه ecma پیکربندی کنید ( "target": "es2018" ). راه حل دیگر می تواند استفاده از قالب های رشته ای به جای توابع باشد:

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