Иногда нужно посетить сайт программным путем. Это может понадобиться по разным причинам. Вот некоторые из них:

  1. Проведение быстрого и точного тестирования поведения сайта без его обновления и проверки вручную. Вы можете автоматизировать взаимодействие с ним, например нажатие кнопок, отправку форм, тестирование пользовательского интерфейса и т. д.
  2. Сбор рендеринга на стороне сервера.
  3. Генерирование скриншотов сайта.
  4. Тестирование расширений Chrome.
  5. Интеграция этого процесса в конвейер тестирования. Вы можете обойтись использованием фреймворков для тестирования, таких как Selenium и Cypress.
  6. Получение более тщательного контроля над браузером программным путем, например принудительное использование GPU для графических вычислений, включение различных рендереров, черновых расширений и т. д.
  7. Создание своих инструментов на основе контента пользовательского интерфейса сайта.

Вам понадобится настроить три основных компонента:

  • Puppeteer;
  • Docker-контейнер (не обязательно);
  • сайт, который вы хотите посетить.

Приведенный ниже код протестирован на Ubuntu 20.04 с графическим процессором Nvidia Tesla.

Puppeteer

Puppeteer  —  это библиотека Node, которая предоставляет высокоуровневый API для управления Chrome или Chromium через DevTools Protocol. По умолчанию библиотека Puppeteer работает в “безголовом” режиме, но может быть настроена для запуска полного (не “безголового”) Chrome или Chromium.

Шаг 1-й: подключение Puppeteer и определение необходимых аргументов

Убедитесь, что вы используете опцию headless (“безголовый” режим). Обратите внимание на то, что на Mac в настоящее время, даже в режиме headless, после запуска экземпляра происходит его быстрая блокировка, поэтому производительность на этой платформе не всегда высокая.

--headless

Шаг 2-й: выбор GL, который должен использовать процесс GPU

Варианты следующие.

  • desktop: любой OpenGL, установленный пользователем (Linux и Mac по умолчанию);
  • egl: любой EGL/GLES2, установленный пользователем (по умолчанию в Windows  —  фактически ANGLE);
  • swiftshader: программный рендерер SwiftShader.
--use-gl=egl

Здесь описаны все возможные опции.

Шаг 3-й: перенаправление журналов консоли из веб-приложения на стандартный вывод puppeteer

Как показано ниже, объект страницы может выполнять перенаправление, подключаясь к консоли, потоку ошибок страницы и т. д.

Обратите внимание на наличие набора аргументов по умолчанию. К ним можно получить доступ с помощью следующей функции. Обязательно проверьте их и переопределите, если что-то не подходит для вашей настройки.

puppeteer.defaultArgs()

Шаг 4-й: запуск Puppeteer

Запустите Puppeteer с помощью функции запуска (launch) и передайте аргументы. Возвращаемый объект  —  это дескриптор браузера.

Используя дескриптор браузера, вы можете начать взаимодействие с сайтом. Сделайте несколько обычных шагов:

  • откройте новую страницу в браузере с помощью функции newpage;
  • получите новый объект page, который имеет функцию goto для посещения сайта.

Можно осуществлять асинхронное взаимодействие, используя функции на дескрипторе страницы, такие как waitforNavigation, которые ждут, пока страница обновится.

Шаг 5-й: закрытие браузера

После завершения работы в браузере не забудьте закрыть его с помощью browser.close().

Docker

Важным условием при установке Docker является передача драйверов Nvidia в контейнер Docker. Один из самых простых способов  —  это основать контейнер на Nvidia open GL image, например:

nvidia/opengl:1.2-glvnd-devel-ubuntu20.04

Полный код приведен ниже. Обязательно установите все зависимости Chrome. Вам не нужно устанавливать Chrome отдельно, на всю систему, так как пакет puppeteer загружает совместимый двоичный файл Chrome. Однако проще получить все зависимости, установив Chrome. Кроме того, необходимо установить любые дополнительные зависимости, которые могут отличаться от приведенных ниже. Здесь установлены основные инструменты Unix, шрифты, node, npm и т. д.

FROM nvidia/opengl:1.2-glvnd-devel-ubuntu20.04

RUN apt-get update
RUN apt-get -y install curl gnupg wget
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
RUN apt-get -y install nodejs

ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Moscow
RUN apt-get install -y tzdata

RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
RUN 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
RUN apt-get install -y vim

ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES all

RUN apt-get install -y --no-install-recommends \
git \
ca-certificates \
build-essential \
g++ \
libxinerama-dev \
libxext-dev \
libxrandr-dev \
libxi-dev \
libxcursor-dev \
libxxf86vm-dev \
libvulkan-dev &&
rm -rf /var/lib/apt/lists/*

RUN apt-get update && apt-get install -y apt-utils && apt-get install -y curl

RUN apt-get update &&
apt-get install -y wget gnupg ca-certificates &&
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 &&
rm -rf /var/lib/apt/lists/* &&
wget --quiet https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh -O /usr/sbin/wait-for-it.sh &&
chmod +x /usr/sbin/wait-for-it.sh

RUN apt-get update &&
apt-get -y --no-install-recommends install ca-certificates \
libcanberra-gtk-module libexif12 pulseaudio attr \
fonts-dejavu-core fonts-freefont-ttf fonts-guru-extra \
fonts-kacst fonts-kacst-one fonts-khmeros-core fonts-lao \
fonts-liberation fonts-lklug-sinhala fonts-lohit-guru \
fonts-nanum fonts-opensymbol fonts-sil-abyssinica \
fonts-sil-padauk fonts-symbola fonts-takao-pgothic \
fonts-tibetan-machine fonts-tlwg-garuda-ttf \
fonts-tlwg-kinnari-ttf fonts-tlwg-laksaman-ttf \
fonts-tlwg-loma-ttf fonts-tlwg-mono-ttf \
fonts-tlwg-norasi-ttf fonts-tlwg-purisa-ttf \
fonts-tlwg-sawasdee-ttf fonts-tlwg-typewriter-ttf \
fonts-tlwg-typist-ttf fonts-tlwg-typo-ttf \
fonts-tlwg-umpush-ttf fonts-tlwg-waree-ttf \
ttf-bitstream-vera ttf-dejavu-core ttf-ubuntu-font-family \
fonts-arphic-ukai fonts-arphic-uming \
fonts-ipafont-mincho fonts-ipafont-gothic \
fonts-unfonts-core &&
rm -rf -- /var/lib/apt/lists /tmp/*.deb

RUN echo 'kernel.unprivileged_userns_clone=1' >/etc/sysctl.d/userns.conf

RUN adduser --disabled-password --gecos '' ubuntu
USER ubuntu

CMD tail -f /dev/null

Фронтальный сервер

После настройки Puppeteer и Docker, можно заходить на любой сайт. Это может быть ваш сайт с отображением каких-либо элементов пользовательского интерфейса. Теперь у вас есть возможность посетить сайт и сделать скриншот, не выходя из своего JavaScript-скрипта 🙂

Приведенный ниже HTML создает простую сцену с помощью Three JS и отображает некоторые шейдеры.

<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
overflow: hidden;
}
</style>
</head>

<body>
<div id="container"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r126/three.min.js"></script>

<script id="vertexShader" type="x-shader/x-vertex">
uniform float time;
uniform vec2 resolution;
void main() {
gl_Position = vec4( position, 1.0 );
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
uniform float time;
uniform vec2 resolution;
void main() {
float x = mod(time + gl_FragCoord.x, 20.) < 10. ? 1. : 0.;
float y = mod(time + gl_FragCoord.y, 20.) < 10. ? 1. : 0.;
gl_FragColor = vec4(vec3(min(x, y)), 1.);
}
</script>
<script>
var container;
var camera, scene, renderer;
var uniforms, material, mesh;
var mouseX = 0, mouseY = 0,
lat = 0, lon = 0, phy = 0, theta = 0;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

init();
var startTime = Date.now();
animate();

function init() {
container = document.getElementById('container');

camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();

uniforms = {
time: { type: "f", value: 1.0 },
resolution: { type: "v2", value: new THREE.Vector2() }
};

material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});

mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
scene.add(mesh);

renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
container.appendChild(renderer.domElement);

uniforms.resolution.value.x = window.innerWidth;
uniforms.resolution.value.y = window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
requestAnimationFrame(animate);
render();
}

function render() {
var elapsedMilliseconds = Date.now() - startTime;
var elapsedSeconds = elapsedMilliseconds / 1000.;
uniforms.time.value = 60. * elapsedSeconds;
renderer.render(scene, camera);
}
</script>
</body>

</html>

Код для посещения сайта после его обслуживания на локальном сервере очень прост:

const page = await browser.newPage(); 
await page.goto(pagePath);
await page.screenshot({path: outputName + '.png'});
await page.pdf({path: outputName + '.pdf'})

В данном примере можно добиться хорошей производительности с 64 одновременными экземплярами puppeteer, работающими на GPU и захватывающими контент сайта. Возможно, есть дополнительные оптимизации в опциях docker и puppeteer для ускорения рендеринга, но они могут быть слишком специфичны для конкретного контента.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Antriani: Visit Websites Without Opening The Browser

Предыдущая статьяSQL для Data Science: альтернатива обмену через Google Disk и Slack
Следующая статьяRust: первые впечатления