CoderCastrov logo
CoderCastrov
JavaScript

Краткое введение в парсинг веб-страниц с использованием Node.js и Puppeteer

Краткое введение в парсинг веб-страниц с использованием Node.js и Puppeteer
просмотров
6 мин чтение
#JavaScript

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

В этом руководстве мы увидим, как парсить данные с исходной веб-страницы, которая не предлагает (легкого в использовании) API.

Для этого руководства я предполагаю, что у вас есть хотя бы базовые знания Node.js, хотя большинство людей должны быть в состоянии следовать приведенным ниже шагам. Мы будем использовать веб-страницу, отображающую программу Амстердам Арены, которая недавно была переименована в Арену Йохана Кройфа (в честь известного футболиста, умершего в 2016 году). В этом примере я собираюсь создать массив словарей, в которых будут храниться название, год, месяц и день всех событий, перечисленных на этом веб-сайте.

Календарь

Johan Cruijff ArenA - место проведения футбольных матчей и концертов. Посмотрите, что произошло за последние месяцы.

www.johancruijffarena.nl

Создание Puppeteer

Первый шаг - создать новую папку, в которой мы создадим новый файл JavaScript. Через терминал найдите вашу новую папку и установите пакет Puppeteer с помощью следующей команды.

npm install --save puppeteer

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

const puppeteer = require(‘puppeteer’);let scrape = async () => {
   const browser = await puppeteer.launch();
   const page = await browser.newPage();   await page.goto(‘https://www.johancruijffarena.nl/calendar.htm');   const result = await page.evaluate(() => {
      var data = [];
      
      var tables = document.querySelectorAll(‘table’);
      data = tables.length;      return data;
   }, );   browser.close();
   return result
}scrape().then((value) => {
   console.log(value);
});

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

node script.js

Если ваш терминал также предоставляет вам целое число, хорошая работа! Давайте обсудим, что означает код и целое число, прежде чем мы продолжим с выводом фактических данных в консоль.

const puppeteer = require(‘puppeteer’);

Строка 1 Здесь мы включаем загруженный пакет Puppeteer в файл скрипта и, следовательно, в проект.

let scrape = async () => {
   const browser = await puppeteer.launch();
   const page = await browser.newPage();   await page.goto(‘https://www.johancruijffarena.nl/calendar.htm');   const result = await page.evaluate(() => {
      var tables = document.querySelectorAll(‘table’);
      var data = tables.length;
   
      return data;
   }, );   browser.close();
   return result
}

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

const browser = await puppeteer.launch();
const page = await browser.newPage();await page.goto(‘https://www.johancruijffarena.nl/calendar.htm');

Строки 4–7 Отсюда мы используем функции, предоставляемые Puppeteer. Создается константа с именем browser, в которой мы открываем новую страницу, как это делаем мы, когда открываем веб-браузер. Затем (вы видите, что я сделал?) браузер переходит на указанную страницу и не продолжает выполнение кода ниже этого оператора до тех пор, пока страница не будет открыта, благодаря ключевым словам async/await.

const result = await page.evaluate(() => {
   var tables = document.querySelectorAll(‘table’);
   var data = tables.length;
   
   return data;
}, );

Строки 9–16 В этих строках мы используем функцию, предлагаемую Puppeteer, а именно page.evaluate. Мы устанавливаем это равным константе result, чтобы мы могли использовать данные, которые мы возвращаем позже, вызывая result. Фактический парсинг (и я думаю, это весело) начинается здесь! Мы сохраняем все элементы таблицы заданного ранее веб-сайта в переменной tables. Я предполагаю, что при использовании Node.js вы понимаете основы HTML и JavaScript, и поэтому это должно быть выполнимо. Переменная с именем data затем инициализируется и устанавливается равной количеству собранных таблиц. Это целое число возвращается и доступно через константу result.

browser.close();
return result

Строки 18–19 После завершения части парсинга браузер закрывается, и возвращается обсуждаемая выше константа result, чтобы ее можно было использовать, когда вызывается переменная парсинга (let).

scrape().then((value) => {
   console.log(value);
});

Строки 22–24 Код в этих строках будет ждать выполнения, пока функция парсинга не завершится из-за ключевого слова then, и фактическое целое число, найденное в строке тринадцать, будет зарегистрировано в терминале.

Парсинг и сохранение данных

Теперь, когда мы обсудили основы этого пакета, вам может быть интересно, почему я выбрал выбрать все элементы таблицы на этом веб-сайте? Из моего опыта работы с Puppeteer (и веб-парсингом в целом) проще выбрать элемент, который включает все данные, которые вы хотите получить, чем парсить разные элементы, содержащие части того, что вы ищете. Если вы откроете веб-сайт и его инструменты разработчика (в Chrome), вы увидите, что каждый элемент таблицы включает заголовок с годом и месяцем и тело с n количеством событий с днем недели, днем в месяце и заголовком. Позвольте мне показать вам следующий код, чтобы показать, насколько удобно это может быть при парсинге данных.

const result = await page.evaluate(() => {   var data = [];   var tables = document.querySelectorAll('table');   for (a = 0; a < tables.length; a++) {
      let monthYear =
      tables[a].children[0].children[0].children[0].innerText;      for (b = 0; b < tables[a].children[1].childElementCount;    
         b++) {            let day =      tables[a].children[1].children[b].children[0].children[0].innerText;            let title = tables[a].children[1].children[b].children[1].children[0].innerText;            let event = { title, monthYear, day };
            data.push(event);
      }
   }   return data;
}, );

Замените приведенный выше код (либо скопируйте и вставьте его, но предпочтительнее введите его самостоятельно) на исходную функцию page.evaluate, которую мы использовали ранее. Запустите свой скрипт и посмотрите, что он выводит в ваш терминал.Отлично, верно? Без API мы все равно можем получить нужные данные с помощью Puppeteer. Позвольте мне провести вас через мой измененный код.

var data = [];

Прежде всего, я создал пустой массив, который позже будет возвращен со всеми словарями, содержащими спарсенные данные.

for (a = 0; a < tables.length; a++) {
      let monthYear =
      tables[a].children[0].children[0].children[0].innerText;   for (b = 0; b < tables[a].children[1].childElementCount;    
      b++) {      let day =      tables[a].children[1].children[b].children[0].children[0].innerText;      let title = tables[a].children[1].children[b].children[1].children[0].innerText;      let event = { title, monthYear, day };
      data.push(event);
   }
}

В приведенном выше коде вы увидите два вложенных цикла for in-range. Первый цикл охватывает все собранные ранее таблицы. Если вы снова откроете инструменты разработчика, вы увидите, что для каждой таблицы год и месяц текста находятся внутри трех элементов: td, tr и thead. Чтобы добраться до текста, который мы действительно хотим спарсить, используйте ключевое слово children, чтобы перейти на следующий уровень. Целое число, которое вы вводите между скобками, определяет, какой номер элемента вы хотите войти (начиная с 0, как и в массиве), видимого из выбранной таблицы. После достижения нужного элемента метод innerText может получить текст этого элемента. Поскольку цикл охватывает каждый отдельный элемент таблицы, все таблицы и, следовательно, все месяцы и годы будут охвачены, как показано на исходном веб-сайте.

Во втором вложенном цикле for in-range мы перебираем отдельные события в каждом определенном месяце. Поскольку это разное для каждого месяца, используется ключевое слово childElementCount, которое помогает определить количество элементов внутри фактической таблицы. Как и в случае с использованием ключевого слова children выше, день месяца и заголовок сохраняются в переменной. В конечном итоге каждое отдельное событие затем сохраняется в словаре с его заголовком, годом, месяцем и днем месяца и добавляется в массив данных.

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

Спасибо за чтение! Не забудьте поставить этому посту несколько аплодисментов с помощью кнопки слева, если вам понравился этот пост и вы хотите увидеть больше. Я публикую статьи о веб-разработке каждую неделю. Пожалуйста, оставьте комментарий и подпишитесь на меня здесь на Medium.