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

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

Зазвичай ви використовуєте веб-скребок для отримання та моніторингу даних і відстеження майбутніх змін у них.

Концепції jQuery, які варто знати перед використанням Cheerio

jQuery є одним із найпопулярніших існуючих пакетів JavaScript. Це полегшує роботу з об’єктною моделлю документа (DOM), обробку подій, анімації тощо. Cheerio — це пакет для веб-скрейпінгу, який побудований на основі jQuery і має той самий синтаксис і API, але полегшує розбір документів HTML або XML.

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

 $("#container");

У наведеному вище блоці коду jQuery вибирає елементи з ідентифікатором «контейнер». Подібна реалізація з використанням звичайного старого JavaScript виглядала б приблизно так:

 document.querySelectorAll("#container");

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

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

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

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

У наведеному вище блоці коду зворотний виклик виконується для кожної ітерації аргументу масиву або об’єкта.

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

Щоб розпочати розбір даних HTML або XML за допомогою Cheerio, ви можете використати метод cheerio.load(). Подивіться на цей приклад:

 const $ = cheerio.load('<html><body><h1>Hello, world!</h1></body></html>');
console.log($('h1').text())

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

 load(content, options, mode)

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

Перегляд хакерських новин за допомогою Cheerio

Код, використаний у цьому проекті, доступний у a Репозиторій 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». Встановіть для 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;">
        <h1 style="text-transform:capitalize">Scraper News</h1>
        <div style="display:flex; gap:10px; align-items:center;">
        <a href="https://www.makeuseof.com/" id="news" onClick='showLoading()'>Home</a>
        <a href="https://wilku.top/best" id="best" onClick='showLoading()'>Best</a>
        <a href="https://wilku.top/newest" id="newest" onClick='showLoading()'>Newest</a>
        <a href="https://wilku.top/ask" id="ask" onClick='showLoading()'>Ask</a>
        <a href="https://wilku.top/jobs" id="jobs" onClick='showLoading()'>Jobs</a>
        </div>
        <p class="loading" style="display:none;">Loading...</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 доступні різні типи публікацій. Є «новини», які є матеріалом на першій сторінці, дописи, які шукають відповіді від інших учасників 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++;
})

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

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

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

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

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

 app.get("https://www.makeuseof.com/", (req, res) => {
    fetchAndRenderPage('/news', res);
})

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

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

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

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

app.listen(PORT)

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

Коли ви відкриваєте термінал і запускаєте npm, запустіть старт. Сервер має запуститися, а потім ви можете відвідати localhost:5500 у своєму браузері, щоб побачити результати.

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

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

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

Ви також можете аналізувати профілі користувачів, щоб аналізувати репутацію користувачів на платформі на основі таких факторів, як отримані голоси «за», зроблені коментарі тощо.