Автоматизируйте скучные вещи

На самом деле я искал способ автоматизировать скучные вещи, которые мне приходилось делать на нескольких веб-страницах, когда я наткнулся на Puppeteer. Да… нет, я говорю не о том чуваке, который манипулирует куклами, а о программе для автоматизации браузера!

Но что такое кукловод?

Согласно документации, Puppeteer — это библиотека Node, предоставляющая высокоуровневый API для управления Chromium или Chrome по протоколу DevTools. Позвольте мне объяснить это простыми словами.

Puppeteer — это библиотека или модуль на Node.js, который позволяет выполнять определенные действия в Интернете, например открывать страницы, перемещаться по веб-сайту, оценивать JavaScript и многое другое автоматически. Он использует Node.js и Chrome, чтобы творить чудеса.

Однако в последней версии Puppeteer можно использовать Firefox вместо Chrome.

Отношения между браузером (Chrome) и Node.js на самом деле закодированы в их логотипе, как вы можете видеть ниже:

Что мне нравится в Puppeteer, так это то, что вы можете легко выполнять парсинг веб-страниц. На самом деле, в этой статье я собираюсь показать вам, как с помощью Puppeteer очистить веб-сайт комиксов для значений URL-адреса src изображения. Круто, правда? 😎

О Кукловоде можно много говорить. Если вам интересно узнать больше, вы можете посетить их официальный сайт документации 👉🏽 здесь.

Достаточно введения. Теперь давайте начнем очищать некоторые данные!

Цель

Как получить исходное значение `src` изображения на веб-сайте? Просто — наведите указатель мыши на изображение, щелкните правой кнопкой мыши, выберите «проверить»… откроется инструмент разработчика, и вот ваше:

Не так много для одного изображения, верно? Но что, если бы вы повторили те же шаги, скажем, для сотни изображений? Это может легко превратиться в кошмар. Поверь мне, я знаю это.

Именно здесь мы собираемся написать скрипт, используя Puppeteer, чтобы автоматически выполнять эту скучную задачу за нас в кратчайшие сроки, сэкономив нам массу времени и избавив от стресса.

Мы будем получать данные с веб-сайта, на котором я обычно читаю комиксы. (Кстати, я люблю читать комиксы. Это из детства… не волнуйтесь 😉)

Веб-сайт

Первое, что вам нужно сделать, это узнать, с какого веб-сайта вы хотите получить данные, и понять, как устроены элементы веб-сайта. Первое не должно быть проблемой. В нашем случае это сайт комиксов, и мы пытаемся извлечь некоторые значения изображения src. Теперь, как мы узнаем, как структурированы изображения? Позвольте мне направить вас:

Зайдите на веб-сайт комиксов. Вас встретит этот интерфейс на главной странице:

Это те изображения, которые нам нужны? Ну, может быть, для вас. Но мы ищем набор изображений любого выпуска любого юмористического названия. Поэтому нам нужно перейти на другую страницу.

Допустим, мы нацелены на название «Бэтмен: Приключения продолжаются (2020)». Мы должны нажать на этот заголовок в разделе «Популярные заголовки» на главной странице.

Если вы не можете найти этот заголовок в разделе «Популярные заголовки» на главной странице веб-сайта во время чтения этой статьи, выполните поиск этого заголовка на веб-сайте или используйте другой заголовок по вашему выбору, и вы будет в состоянии следовать за просто отлично. Шаблон остается прежним.

Теперь мы находимся на странице обзора. На этой странице у нас есть список всех выпусков, доступных для этого комикса — в нашем случае «Бэтмен: Приключения продолжаются (2020)».

Помните, что цель состоит в том, чтобы добраться до изображений (или страниц) заголовка комикса issue.

Чтобы попасть на страницу «книжные страницы», мы должны выбрать выпуск. Итак, давайте выберем «Batman The Adventures Continue 2020 Issue #1».

Теперь мы здесь! 😎

Вы быстро заметите общий шаблон навигации по страницам книг на этом веб-сайте.

Я полагаю, что вы наблюдали, как мы перешли на эту страницу? Мы проинструктируем Puppeteer следовать тем же маршрутом. И мы также расскажем Puppeteer, как найти нужные изображения.

Теперь давайте погрузимся в Puppeteer!

Робот

Хорошо, во-первых, убедитесь, что у вас установлены Node и NPM — Puppeteer сильно зависит от них. Узнайте, как скачать установщик Node.js и NPM здесь.

Следующее, что нужно сделать, это инициализировать новый проект node.js. Для этого создайте новую папку и назовите ее как хотите. В моем случае я назову его Puppeteer-Tutorial. Откройте терминал и cd во вновь созданный каталог.

Пссс: вы можете найти код этой статьи на Github.

Теперь запустите:

npm init

При запуске этой команды вам будет предложено ввести некоторую важную информацию о вашем новом проекте. Продолжайте и делайте именно так, пропуская те, которые вам действительно не нужны (я оставлю вам решать). В итоге у вас должен получиться знакомый результат:

Это просто означает, что ваш проект был инициализирован файлом package.json, содержащим всю информацию, которую вы ему передали ранее.

Теперь пришло время установить Puppeteer. Но перед установкой Puppeteer вы должны открыть свой проект в редакторе кода (желательно VS Code) и создать файл index.js на том же уровне пути, что и ваш файл package.json.

Затем запустите:

npm install puppeteer

Это установит Puppeteer из библиотеки NPM. Вы также заметите, что он загружает последнюю версию Chromium (~ 170 МБ для Mac, ~ 282 МБ для Linux, ~ 280 МБ для Win), которая гарантированно работает с API.

В итоге у вас должен получиться список из 3-х файлов, а именно: index.js, package-lock.json и package.json, и папку node_modules. Когда вы открываете файл package.json, вы должны увидеть вновь созданный объектно-подобный объект dependencies с парой "puppeteer": "<lastest version>" внутри.

Теперь потрите ладони друг о друга, потому что мы собираемся протестировать Puppeteer в первый раз! Разве это не захватывающе? 😃

Для запуска теста воспользуемся типовым примером из документации.

Создайте новый файл test.js и вставьте в него следующий код:

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto("https://kenjimmy.me");
  await page.screenshot({ path: "example.png" });

  await browser.close();
})();

Теперь откройте встроенный терминал в VS Code, нажав ctrl (cmd) + `. Затем запустите:

node test.js

node — это исполняемая команда, поскольку на вашем компьютере установлен Node.js. Таким образом, node test.js выполняет код JavaScript (в данном случае, в частности, код Puppeteer) внутри нашего файла test.js в среде выполнения Node.js.

Я не буду объяснять, что делает каждая строка кода в нашем файле test.js, потому что я просто хочу проверить, правильно ли установлен Puppeteer. Мы сохраняем объяснение для фактического сценария, который вскоре последует.

При выполнении этой команды вы должны увидеть файл изображения со скриншотом домашней страницы моего сайта размером 800×600 пикселей.

Теперь мы можем идти.

Очистка изображений Srcs с помощью Puppeteer

Внутри вашего файла index.js давайте потребуем модуль fs (файловая система). Это позволит вам записывать данные, полученные с сайта комиксов, в файл.

const fs = require("fs");

Поскольку вы записываете некоторые данные в файл, от вас требуется уже создать этот файл. Итак, продолжайте и создайте файл data.json.

Помимо файловой системы, нам обязательно нужно импортировать или потребовать Puppeteer. Следующий код будет делать именно так:

const puppeteer = require("puppeteer");

В JavaScript есть концепция, известная как выражение немедленно вызываемой функции (IIFE). Проще говоря, IIFE — это функция JavaScript, которая запускается, как только она определена. Мы собираемся использовать IIFE в качестве оболочки для нашего сценария, что является распространенным способом написания сценариев Puppeteer.

Вот как мы пишем IIFE:

(async () => {
 // your code goes here...
})();

Обратите внимание, что нашей анонимной функции предшествует ключевое слово theasync. Это специальный синтаксис, введенный в JavaScript начиная с ES8 для более удобной работы с промисами. Он ожидает оператор await, и мы собираемся предоставить его в один миг.

Теперь внутри нашего IFFE напишем оператор try...catch. Это поможет нам правильно обрабатывать ошибки, если они были.

(async () => {
  try {
    // ...
  } catch (error) {
    console.log(error);
  }
})();

Все, что мы делаем в блоке catch, — это выводим error на консоль, если он ее поймал.

Теперь давайте напишем наш скрипт внутри блока try.

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

// ...

// Initialize Puppeteer
const browser = await puppeteer.launch();
const page = await browser.newPage();

// Specify comic issue page url
await page.goto(
      "https://comicpunch.net/readme/index.php?title=batman-the-adventures-continue-2020&chapter=1"
);
console.log("page has been loaded!");

// ...

Откуда мы взяли URL-адрес, который мы передали в page.goto()?

Помните титульный лист комикса? Нам просто нужно скопировать одну из ссылок задачи:

Мы только что сказали Puppeteer запустить браузер Chromium, используя метод .launch(), и перейти по указанному URL-адресу, используя методы .newPage() и .goto().

Что делать дальше? Следует нажать на кнопку «Полная глава» на странице:

Нажатие на кнопку «Полная глава» отобразит все нужные нам изображения на странице. И это именно то, чего мы хотим.

Нам нужен способ сообщить Puppeteer, где находится кнопка «Полная глава», и нажать на нее. Таким образом, нам нужно использовать .click(). Этот метод ожидает селектор.

Мы можем получить селектор любого элемента, просмотрев его в браузере. В этом случае мы будем ориентироваться на «button.button4», где «.button4» — это имя класса, присвоенное кнопке «Полная глава».

// ...

// While page is waiting for 1s, click on the 'Full Chapter' button and do the rest
await page.waitFor(1000);
await page.click("button.button4");
console.log("'Full Chapter' button has been clicked!");

// ...

О, парень! Перед .click() есть строка. Да, мы должны использовать .waitFor() и передать 1000 мс, потому что нам нужно немного больше времени, чтобы остальная часть кода заработала, как задумано. Если вы хотите поэкспериментировать с этим, удалите эту строку и посмотрите, что произойдет. 😁

Кстати, функция .waitFor() устарела и будет удалена в следующем выпуске.

Теперь давайте скажем Puppeteer оценить или вычислить основную задачу, состоящую в том, чтобы преобразовать список узлов изображений, возвращаемых из DOM, в массив, затем сопоставить каждый элемент и получить значение атрибута src и сохранить его в переменной src, которая поэтому возвращается как значение переменной issueSrcs.

.evaluate() принимает функцию для оценки в контексте страницы. Итак, здесь мы пишем наш основной код для получения всех URL-адресов изображений с классом '.comicpic':

// ...

const issueSrcs = await page.evaluate(() => {
      const srcs = Array.from(
        document.querySelectorAll(".comicpic")
      ).map((image) => image.getAttribute("src"));
      return srcs;
});
console.log("Page has been evaluated!");

// ...

Теперь все наши данные хранятся в нашей переменной «issueSrcs». Но мы еще не закончили. Нам нужно сохранить данные в файле data.json, верно? Здесь мы используем метод fs.writeFileSync():

// ...

// Persist data into data.json file
fs.writeFileSync("./data.json", JSON.stringify(issueSrcs));
console.log("File is created!");

// ...

Наконец, мы заканчиваем Кукловода, используя метод .close(). В противном случае? Ну...😆

// ...

// End Puppeteer
await browser.close();

// ...

В конце концов, ваш код капитального ремонта должен выглядеть так:

const fs = require("fs");
const puppeteer = require("puppeteer");

(async () => {
  try {
    // Initialize Puppeteer
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    // Specify comic issue page url
    await page.goto(
      "https://comicpunch.net/readme/index.php?title=batman-the-adventures-continue-2020&chapter=1"
    );
    console.log("page has been loaded!");

    // While page is waiting for 1s, click on the 'Full Chapter' button and do the rest
    await page.waitFor(1000);
    await page.click("button.button4");
    console.log("'Full Chapter' button has been clicked!");

    // Evaluate/Compute the main task:
    // Here, we convert the Nodelist of images returned from the DOM into an array, then map each item and get the src attribute value, and store it in 'src' variable, which is therefore returned to be the value of 'issueSrcs' variable.
    const issueSrcs = await page.evaluate(() => {
      const srcs = Array.from(
        document.querySelectorAll(".comicpic")
      ).map((image) => image.getAttribute("src"));
      return srcs;
    });
    console.log("Page has been evaluated!");

    // Persist data into data.json file
    fs.writeFileSync("./data.json", JSON.stringify(issueSrcs));
    console.log("File is created!");

    // End Puppeteer
    await browser.close();
  } catch (error) {
    console.log(error);
  }
})();

Прохладный! Мы готовы опробовать наш скрипт. Мне интересно. А вы? 😃

Теперь в вашем терминале запустите:

node index.js

И… ура! Мы сделали это!! Да, мы успешно извлекли все URL-адреса комических изображений с классом «.comicpic» на этой странице и сохранили их в формате JSON в нашем файле data.json. Прекрасная работа. Отличная работа! Вы только что улучшили свой IQ на 10. (очевидно, это была шутка)

Вы должны увидеть что-то подобное, когда откроете файл data.json:

Вывод

Puppeteer — отличный инструмент для веб-автоматизации и даже веб-тестирования. Большинство вещей, которые вы можете сделать вручную в браузере, можно сделать с помощью Puppeteer!

В этой статье мы поэкспериментировали с ним, собрав некоторые значения src изображений с веб-страницы, но вот еще несколько примеров, которые вы можете попробовать сами:

  • Просканируйте SPA (одностраничное приложение) и сгенерируйте предварительно отрендеренный контент (т. е. «SSR» (рендеринг на стороне сервера)).
  • Автоматизируйте отправку форм, тестирование пользовательского интерфейса, ввод с клавиатуры и т. д.
  • Создайте современную автоматизированную среду тестирования. Запускайте тесты непосредственно в последней версии Chrome, используя новейшие функции JavaScript и браузера.
  • Запишите временную трассировку вашего сайта, чтобы помочь диагностировать проблемы с производительностью.
  • Протестируйте расширения Chrome.

Получите максимум от этого удивительного инструмента! (а почему бы и нет 😎)

Первоначально опубликовано на https://kenjimmy.me.