Устранение неполадок с Кукловодом

Headless Chrome не запускается в Windows

Некоторые политики Chrome могут требовать запуска Chrome или Chromium с определенными расширениями.

Puppeteer по умолчанию передает флаг --disable-extensions и поэтому не запускается, когда такие политики активны.

Чтобы обойти эту проблему, попробуйте запустить без флага:

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

Контекст: проблема 3681 .

Headless 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

Посмотрите обсуждения:

  • #290 — Устранение неполадок Debian
  • #391 — Устранение неполадок CentOS
  • #379 - Устранение неполадок Alpine

Безголовый Chrome отключает компоновку графического процессора

Chrome и Chromium требуют --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 v14.0.0, из-за которой произошел сбой extract-zip — модуля Puppeteer, который используется для извлечения загрузок из браузера в нужное место. Ошибка была исправлена ​​в Node.js v14.1.0, поэтому убедитесь, что вы используете эту версию или выше.

Настройте песочницу Chrome Linux

Чтобы защитить среду хоста от ненадежного веб-контента, Chrome использует несколько уровней песочницы . Чтобы это работало правильно, сначала необходимо настроить хост. Если для Chrome нет подходящей песочницы, произойдет сбой с ошибкой No usable sandbox! .

Если вы абсолютно доверяете контенту, который открываете в Chrome, вы можете запустить Chrome с аргументом --no-sandbox :

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

Есть два способа настроить песочницу в Chromium.

Клонирование пространства имен Sser поддерживается только современными ядрами. Пространства имен непривилегированных пользователей, как правило, можно включить, но они могут открыть больше возможностей для атак на ядро ​​для (не изолированных) процессов без полномочий root для повышения до привилегий ядра.

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

Возможно, вы захотите экспортировать переменную 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 (v5.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

  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) Вероятно, это вызвано тем, что Jest автоматически определяет количество процессов на всей машине ( 36 ), а не количество, разрешенное для вашего контейнера ( 2 ). Чтобы это исправить, установите jest --maxWorkers=2 в вашей тестовой команде.

Запустите Puppeteer в Docker

Запустить headless 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 .

Пример файла 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 размером 64 МБ. Обычно оно слишком мало для Chrome и приводит к сбою Chrome при рендеринге больших страниц. Чтобы исправить это, запустите контейнер с помощью docker run --shm-size=1gb , чтобы увеличить размер /dev/shm . Начиная с Chrome 65, в этом больше нет необходимости. Вместо этого запустите браузер с флагом --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 при локальной разработке. Поскольку Dockerfile добавляет пользователя pptr как непривилегированного пользователя, у него может не быть всех необходимых привилегий.

Dum-init стоит проверить, если у вас много процессов-зомби в Chrome. Для процессов с PID=1 предусмотрена специальная обработка, что в некоторых случаях затрудняет правильное завершение Chrome (например, с Docker).

Запустите Puppeteer в облаке

В Google App Engine

Среда выполнения Node.js стандартной среды App Engine поставляется со всеми системными пакетами, необходимыми для запуска Headless Chrome.

Чтобы использовать puppeteer , укажите модуль как зависимость в package.json и разверните его в Google App Engine. Узнайте больше об использовании puppeteer в App Engine, следуя официальному руководству .

О функциях Google Cloud

Среда выполнения Google Cloud Functions Node.js 10 поставляется со всеми системными пакетами, необходимыми для запуска Headless Chrome.

Чтобы использовать puppeteer , укажите модуль как зависимость в вашем package.json и разверните свою функцию в Google Cloud Functions, используя среду выполнения nodejs10 .

Запустите Puppeteer в Google Cloud Run

Среда выполнения Node.js по умолчанию для Google Cloud Run не включает системные пакеты, необходимые для запуска Headless Chrome. Настройте свой собственный Dockerfile и включите недостающие зависимости .

На Хероку

Запуск Puppeteer на Heroku требует некоторых дополнительных зависимостей, которых нет в Linux-системе, которую Heroku запускает для вас. Чтобы добавить зависимости при развертывании, добавьте пакет сборки Puppeteer Heroku в список пакетов сборки для вашего приложения в разделе «Настройки» > «Пакеты сборки».

URL-адрес пакета сборки: https://github.com/jontewks/puppeteer-heroku-buildpack .

Убедитесь, что вы используете режим '--no-sandbox' при запуске Puppeteer. Это можно сделать, передав его в качестве аргумента вызову .launch() : puppeteer.launch({ args: ['--no-sandbox'] }); .

Когда вы нажмете «Добавить пакет сборки», вставьте этот URL-адрес во входные данные и нажмите «Сохранить» . При следующем развертывании ваше приложение также установит зависимости, необходимые для запуска Puppeteer.

Если вам нужно визуализировать китайские, японские или корейские символы, вам может потребоваться использовать сборочный пакет с дополнительными файлами шрифтов, например https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack.

Также есть еще одно руководство от @timleland , включающее пример проекта.

На AWS Лямбда

AWS Lambda ограничивает размер пакета развертывания примерно 50 МБ. Это создает проблемы при запуске Chrome без монитора (и, следовательно, Puppeteer) на Lambda. Сообщество собрало несколько ресурсов, которые решают эти проблемы:

Экземпляр AWS EC2 под управлением Amazon-Linux

Если в вашем конвейере CI/CD есть экземпляр EC2, на котором работает amazon-linux, и вы хотите запускать тесты Puppeteer в amazon-linux, выполните следующие действия.

  1. Чтобы установить Chromium, необходимо сначала включить amazon-linux-extras , который является частью EPEL (дополнительные пакеты для корпоративного Linux) :

    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 и многих других пакетов.

Проблемы с транспиляцией кода

Если вы используете транспилятор JavaScript, такой как Babel или TypeScript, вызов evaluate() с асинхронной функцией может не работать. Это связано с тем, что puppeteer использует Function.prototype.toString() для сериализации функций, в то время как транспиляторы могут изменять выходной код таким образом, что это несовместимо с puppeteer .

Некоторыми обходными путями этой проблемы могут быть указания транспилятору не портить код, например, настроить TypeScript на использование последней версии ecma ( "target": "es2018" ). Другим обходным решением может быть использование строковых шаблонов вместо функций:

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