Úvod do Web Scraping s Cheerio
Web scraping představuje techniku, která umožňuje extrahovat data z konkrétní webové stránky. Webové stránky pro strukturování svého obsahu využívají HTML. Pokud je kód HTML čistý a sémantický, je poměrně snadné s jeho pomocí vyhledávat užitečné informace.
Pro získávání, sledování dat a monitorování budoucích změn se obvykle používá nástroj pro web scraping.
Základy jQuery, které byste měli znát před použitím Cheerio
jQuery patří k nejpopulárnějším knihovnám JavaScriptu. Zjednodušuje práci s Document Object Modelem (DOM), zpracování událostí, animace a další. Cheerio je knihovna pro web scraping, která staví na jQuery – sdílí stejnou syntaxi a API a zároveň usnadňuje zpracování HTML nebo XML dokumentů.
Než začnete používat Cheerio, je nezbytné rozumět, jak vybírat HTML elementy pomocí jQuery. Naštěstí jQuery podporuje většinu selektorů CSS3, což usnadňuje výběr elementů z DOM. Podívejte se na následující ukázku kódu:
$("#container");
V uvedeném bloku kódu jQuery vybírá elementy s ID "container". Ekvivalentní implementace v čistém JavaScriptu by vypadala takto:
document.querySelectorAll("#container");
Při porovnání těchto dvou fragmentů je vidět, že první blok je mnohem čitelnější. V tom spočívá výhoda jQuery.
jQuery také nabízí užitečné metody jako text(), html() a další pro manipulaci s HTML elementy. K dispozici je několik metod pro procházení DOM, například parent(), siblings(), prev() a next().
Metoda every() v jQuery se často používá v projektech s Cheerio. Umožňuje iterovat přes objekty a pole. Syntaxe metody every() je následující:
$(<element>).each(<array or object>, callback)
V tomto kódu se funkce `callback` spustí pro každou iteraci pole nebo objektu.
Načítání HTML pomocí Cheerio
Pro zahájení zpracování HTML nebo XML dat pomocí Cheerio můžete použít metodu `cheerio.load()`. Podívejte se na tento příklad:
const $ = cheerio.load('<html><body><h1>Ahoj světe!</h1></body></html>');
console.log($('h1').text())
Tento kód používá metodu `text()` z jQuery pro získání textového obsahu elementu `h1`. Kompletní syntaxe metody `load()` je:
load(content, options, mode)
Parametr `content` odkazuje na samotná HTML nebo XML data, která se předávají metodě `load()`. Parametr `options` je volitelný objekt, který může upravit chování metody. Metoda `load()` ve výchozím nastavení automaticky přidá elementy `html`, `head` a `body`, pokud chybí. Pokud chcete toto chování potlačit, musíte nastavit režim na `false`.
Škrábání Hacker News pomocí Cheerio
Kód použitý v tomto projektu je k dispozici v úložišti GitHub a můžete jej využívat zdarma pod licencí MIT.
Je čas spojit všechny nabyté znalosti a vytvořit jednoduchý webový scraper. Hacker News je populární web pro podnikatele a inovátory. Je to také ideální webová stránka pro praktické procvičení dovedností v oblasti web scrapingu, protože se rychle načítá, má velmi jednoduché rozhraní a neobsahuje reklamy.
Ujistěte se, že máte na počítači nainstalované Node.js a Node Package Manager. Vytvořte prázdnou složku, soubor `package.json` a do tohoto souboru přidejte následující 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"
}
}
Poté otevřete terminál a spusťte:
npm i
Tím se nainstalují všechny potřebné závislosti pro sestavení scraperu. Mezi tyto balíčky patří Cheerio pro zpracování HTML, ExpressJS pro vytvoření serveru a jako vývojová závislost Nodemon – nástroj, který monitoruje změny v projektu a automaticky restartuje server.
Nastavení a vytvoření potřebných funkcí
Vytvořte soubor `index.js` a v něm definujte konstantní proměnnou s názvem `PORT`. Nastavte ji na hodnotu 5500 (nebo jiné vámi zvolené číslo) a poté importujte knihovny Cheerio a Express.
const PORT = 5500;
const cheerio = require("cheerio");
const express = require("express");
const app = express();
Dále definujte tři proměnné: `url`, `html` a `finishedPage`. Proměnnou `url` nastavte na adresu Hacker News.
const url="https://news.ycombinator.com";
let html;
let finishedPage;
Nyní vytvořte funkci `getHeader()`, která vrátí HTML kód, který by měl prohlížeč vykreslit.
function getHeader(){
return `
<div style="display:flex; flex-direction:column; align-items:center;">
<h2 style="text-transform:capitalize">Scraper News</h2>
<div style="display:flex; gap:10px; align-items:center;">
<a href="https://www.makeuseof.com/" id="news" onClick='showLoading()'>Domů</a>
<a href="https://wilku.top/best" id="best" onClick='showLoading()'>Nejlepší</a>
<a href="https://wilku.top/newest" id="newest" onClick='showLoading()'>Nejnovější</a>
<a href="https://wilku.top/ask" id="ask" onClick='showLoading()'>Zeptat se</a>
<a href="https://wilku.top/jobs" id="jobs" onClick='showLoading()'>Práce</a>
</div>
<p class="loading" style="display:none;">Načítání...</p>
</div>
`}
Vytvořte další funkci `getScript()`, která vrátí JavaScript, který se spustí v prohlížeči. Ujistěte se, že předáváte proměnnou `type` jako argument při volání funkce.
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>`
}
Nakonec vytvořte asynchronní funkci s názvem `fetchAndRenderPage()`. Tato funkce provádí web scraping na stránce Hacker News, analyzuje ji a formátuje pomocí Cheerio a poté odesílá HTML zpět klientovi k vykreslení.
async function fetchAndRenderPage(type, res) {
const response = await fetch(`${url}${type}`)
html = await response.text();
}
Na Hacker News jsou dostupné různé typy příspěvků. „Zprávy“ představují obsah titulní stránky, příspěvky, které hledají odpovědi od ostatních uživatelů Hacker News, mají štítek „zeptat se“. Populární příspěvky jsou označeny štítkem „nejlepší“, nejnovější příspěvky štítkem „nejnovější“ a příspěvky s nabídkami práce mají štítek „práce“.
Funkce `fetchAndRenderPage()` načte seznam příspěvků z Hacker News na základě typu zadaného jako argument. Pokud je operace načtení úspěšná, funkce uloží text odpovědi do proměnné `html`.
Dále do funkce přidejte následující řádky:
res.set('Content-Type', 'text/html');
res.write(getHeader());const $ = cheerio.load(html);
const articles = [];
let i = 1;
V uvedeném bloku kódu metoda `set()` nastavuje pole HTTP hlaviček. Metoda `write()` je zodpovědná za odeslání části těla odpovědi. Funkce `load()` přijímá `html` jako argument.
Dále přidejte následující řádky pro výběr všech potomků elementů s třídou `titleline`.
$('.titleline').children('a').each(function(){
let title = $(this).text();
articles.push(`<h4>${i}. ${title}</h4>`);
i++;
})
V tomto bloku kódu každá iterace extrahuje textový obsah cílového HTML elementu a ukládá jej do proměnné `title`.
Dále vložte odpověď z funkce `getScript()` do pole `articles`. Následně vytvořte proměnnou `finishedPage`, která bude obsahovat kompletní HTML kód pro odeslání prohlížeči. Nakonec pomocí metody `write()` odešlete hotovou stránku a ukončete proces odpovědi metodou `end()`.
articles.push(getScript(type))
finishedPage = articles.reduce((c, n) => c + n);
res.write(finishedPage);
res.end();
Definování tras pro zpracování GET požadavků
Přímo pod funkcí `fetchAndRenderPage` použijte metodu `express get()` pro definování příslušných tras pro různé typy příspěvků. Poté použijte metodu `listen()` pro naslouchání připojení k určenému portu v lokální síti.
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)
V uvedeném bloku kódu má každá metoda `get` funkci callback, která volá funkci `fetchAndRenderPage`, předávající příslušné typy a objekt `res`.
Když otevřete terminál a spustíte `npm start`, server se spustí. Poté můžete ve svém prohlížeči navštívit `localhost:5500` a zobrazit výsledky.
Gratuluji, právě se vám podařilo provést web scraping Hacker News a načíst názvy příspěvků bez potřeby externího API.
Další možnosti web scrapingu
S daty získanými z Hacker News můžete vytvářet různé vizualizace, jako jsou tabulky, grafy a mračna slov, a prezentovat tak statistiky a trendy v srozumitelnější formě.
Můžete také provádět scraping uživatelských profilů a analyzovat reputaci uživatelů na platformě na základě faktorů, jako jsou získané hlasy, komentáře a další.