Вступ до веб-збирання за допомогою Cheerio

Збирання даних з веб-сайтів, часто називане веб-скрейпінгом, являє собою метод видобування інформації зі сторінок інтернету. Веб-сторінки побудовані за допомогою HTML, мови розмітки, що структурує контент. Якщо HTML-код є чітким та семантичним, його аналіз та вилучення потрібних даних стає простішим завданням.

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

Основи jQuery, важливі для розуміння перед початком роботи з Cheerio

jQuery – це широко відома бібліотека JavaScript, що значно спрощує взаємодію з DOM (Document Object Model), обробку подій, анімацію та інше. Cheerio, у свою чергу, є бібліотекою для веб-скрейпінгу, створеною на основі jQuery. Вона використовує аналогічний синтаксис та API, але спеціалізується на аналізі HTML або XML документів.

Перед тим, як зануритися у використання Cheerio, важливо розуміти, як jQuery обирає HTML-елементи. jQuery підтримує більшість селекторів CSS3, що дозволяє легко знаходити елементи в DOM. Розгляньмо приклад:

 $("#container");

У цьому прикладі jQuery вибирає елемент з ідентифікатором “container”. Аналогічна дія з використанням ванільного JavaScript виглядала б наступним чином:

 document.querySelectorAll("#container");

Порівнюючи ці два блоки коду, можна помітити, що перший варіант набагато простіший для читання. У цьому і полягає сила jQuery.

jQuery також має корисні методи, такі як text(), html() та інші, які дають змогу маніпулювати HTML-елементами. Для навігації по DOM доступні методи parent(), siblings(), prev() та next().

Метод each() у jQuery часто використовується в проектах Cheerio. Він дозволяє перебирати об’єкти та масиви. Синтаксис методу each() виглядає так:

 $(<element>).each(<array or object>, callback)

В цьому коді функція callback виконується для кожного елемента масиву або об’єкта.

Завантаження HTML за допомогою Cheerio

Для початку аналізу HTML або XML даних за допомогою Cheerio, використовується метод cheerio.load(). Розглянемо приклад:

 const $ = cheerio.load('<html><body><h2>Привіт, світ!</h2></body></html>');
console.log($('h2').text())

Цей код використовує метод jQuery text() для вилучення текстового вмісту елемента h2. Повний синтаксис методу load() виглядає так:

 load(content, options, mode)

Параметр content – це HTML або XML дані, які передаються до методу load(). Параметр options – це необов’язковий об’єкт, що може змінювати поведінку методу. За замовчуванням метод load() автоматично додає елементи html, head та body, якщо вони відсутні. Щоб відключити цю поведінку, потрібно встановити для режиму значення false.

Створення скрейпера для Hacker News з Cheerio

Код цього проєкту знаходиться у репозиторії GitHub і є доступним для вільного використання під ліцензією MIT.

Настав час об’єднати всі отримані знання та створити простий веб-скрейпер. Hacker News – популярний ресурс для підприємців та інноваторів. Він швидко завантажується, має лаконічний інтерфейс та не містить реклами, що робить його ідеальним для практики веб-скрейпінгу.

Переконайтеся, що на вашому комп’ютері встановлено Node.js та Node Package Manager. Створіть порожню папку, потім файл package.json та додайте до нього наступний JSON:

 {
  "name": "web-scraper",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "cheerio": "^1.0.0-rc.12",
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

Після цього відкрийте термінал та виконайте команду:

 npm i

Це встановить необхідні залежності для створення скрейпера. Серед них: Cheerio для аналізу HTML, ExpressJS для створення сервера та Nodemon, утиліта, яка відстежує зміни в проекті та автоматично перезапускає сервер.

Налаштування та створення потрібних функцій

Створіть файл index.js та оголосіть в ньому константу PORT. Призначте їй значення 5500 (або інше). Імпортуйте бібліотеки Cheerio та Express.

 const PORT = 5500;
const cheerio = require("cheerio");
const express = require("express");
const app = express();

Далі визначте три змінні: url, html та finishedPage. Змінній url присвойте URL-адресу Hacker News.

 const url="https://news.ycombinator.com";
let html;
let finishedPage;

Створіть функцію getHeader(), яка повертає HTML-код для відображення в браузері.

 function getHeader(){
    return `
        <div style="display:flex; flex-direction:column; align-items:center;">
        <h2 style="text-transform:capitalize">Новини Скрепера</h2>
        <div style="display:flex; gap:10px; align-items:center;">
        <a href="https://www.makeuseof.com/" id="news" onClick='showLoading()'>Головна</a>
        <a href="https://wilku.top/best" id="best" onClick='showLoading()'>Кращі</a>
        <a href="https://wilku.top/newest" id="newest" onClick='showLoading()'>Нові</a>
        <a href="https://wilku.top/ask" id="ask" onClick='showLoading()'>Запитання</a>
        <a href="https://wilku.top/jobs" id="jobs" onClick='showLoading()'>Вакансії</a>
        </div>
        <p class="loading" style="display:none;">Завантаження...</p>
        </div>
`}

Створіть функцію getScript(), яка повертає JavaScript код для виконання в браузері. Тип змінної передається як аргумент під час виклику.

 function getScript(type){
    return `
    <script>
    document.title = "${type.substring(1)}"

    window.addEventListener("DOMContentLoaded", (e) => {
      let navLinks = [...document.querySelectorAll("a")];
      let current = document.querySelector("#${type.substring(1)}");
      document.body.style = "margin:0 auto; max-width:600px;";
      navLinks.forEach(x => x.style = "color:black; text-decoration:none;");
      current.style.textDecoration = "underline";
      current.style.color = "black";
      current.style.padding = "3px";
      current.style.pointerEvents = "none";
    })

    function showLoading(e){
      document.querySelector(".loading").style.display = "block";
      document.querySelector(".loading").style.textAlign = "center";
    }
    </script>`
}

Створіть асинхронну функцію fetchAndRenderPage(). Вона очищує сторінку Hacker News, аналізує та форматує її за допомогою Cheerio, потім надсилає HTML клієнту.

 async function fetchAndRenderPage(type, res) {
    const response = await fetch(`${url}${type}`)
    html = await response.text();
}

На Hacker News є різні типи публікацій: “новини” – матеріали головної сторінки, “запитання” – дописи, де користувачі шукають відповіді, “найкращі” – популярні дописи, “найновіші” – останні дописи, та “вакансії” – оголошення про роботу.

Функція fetchAndRenderPage() отримує список дописів зі сторінки Hacker News, ґрунтуючись на переданому типі. Якщо запит успішний, змінна html зберігає текст відповіді.

Далі додайте наступний код у функцію:

 res.set('Content-Type', 'text/html');
res.write(getHeader());

const $ = cheerio.load(html);
const articles = [];
let i = 1;

Метод set() встановлює поле заголовка HTTP. Метод write() відправляє частину тіла відповіді. Функція load() приймає html як аргумент.

Далі додайте код для вибору дочірніх елементів всіх елементів з класом “titleline”.

 $('.titleline').children('a').each(function(){
    let title = $(this).text();
    articles.push(`<h4>${i}. ${title}</h4>`);
    i++;
})

Тут кожна ітерація отримує текстовий вміст елемента та зберігає його в змінній title.

Надішліть відповідь, використовуючи функцію getScript() та масив articles. Створіть змінну finishedPage, що містить готовий HTML для відправки в браузер. Завершіть процес відповіді методами write() та end().

 articles.push(getScript(type))
finishedPage = articles.reduce((c, n) => c + n);
res.write(finishedPage);
res.end();

Визначення маршрутів для обробки GET запитів

Безпосередньо після функції fetchAndRenderPage, використовуйте метод get() з Express, щоб визначити маршрути для різних типів публікацій. Використайте метод listen() для прослуховування з’єднання на вказаному порту.

 app.get("/", (req, res) => {
    fetchAndRenderPage('/news', res);
})

app.get("/best", (req, res) => {
    fetchAndRenderPage("/best", res);
})

app.get("/newest", (req, res) => {
    fetchAndRenderPage("/newest", res);
})

app.get("/ask", (req, res) => {
    fetchAndRenderPage("/ask", res);
})

app.get("/jobs", (req, res) => {
    fetchAndRenderPage("/jobs", res);
})

app.listen(PORT)

Кожен метод get має функцію зворотного виклику, що викликає функцію fetchAndRenderPage з відповідними типами та об’єктами res.

Запустіть сервер, виконавши команду “npm start” у терміналі. Після цього відкрийте localhost:5500 у браузері.

Вітаємо, ви успішно створили веб-скрейпер, що отримує заголовки дописів з Hacker News без використання зовнішніх API.

Подальші можливості веб-скрейпінгу

Дані, отримані з Hacker News, можна використовувати для створення візуалізацій, таких як діаграми, графіки та хмари слів. Це допоможе представити статистику та тенденції у більш зрозумілому форматі.

Можна також аналізувати профілі користувачів, щоб оцінювати їх репутацію на основі кількості отриманих голосів, залишених коментарів та інших факторів.