En introduktion till webbskrapning med Cheerio

Webbskrapning är en teknik som gör det möjligt att få data från en viss webbplats. Webbplatser använder HTML för att beskriva sitt innehåll. Om HTML-koden är ren och semantisk är det lätt att använda den för att hitta användbar data.

Du kommer vanligtvis att använda en webbskrapa för att hämta och övervaka data och spåra framtida ändringar av den.

jQuery-koncept värda att veta innan du använder Cheerio

jQuery är ett av de mest populära JavaScript-paketen som finns. Det gör det enklare att arbeta med Document Object Model (DOM), hantera händelser, animationer och mer. Cheerio är ett paket för webbskrapning som bygger ovanpå jQuery – som delar samma syntax och API, samtidigt som det gör det lättare att analysera HTML- eller XML-dokument.

Innan du lär dig hur man använder Cheerio är det viktigt att veta hur man väljer HTML-element med jQuery. Tack och lov stöder jQuery de flesta CSS3-väljare vilket gör det lättare att ta tag i element från DOM. Ta en titt på följande kod:

 $("#container");

I kodblocket ovan väljer jQuery elementen med id:t ”container”. En liknande implementering med vanlig gammal JavaScript skulle se ut ungefär så här:

 document.querySelectorAll("#container");

Om du jämför de två sista kodblocken kan du se att det förra kodblocket är mycket lättare att läsa än det senare. Det är det fina med jQuery.

jQuery har också användbara metoder som text(), html() och mer som gör det möjligt att manipulera HTML-element. Det finns flera metoder du kan använda för att gå igenom DOM, som parent(), siblings(), prev() och next().

Metoden each() i jQuery är mycket populär i många Cheerio-projekt. Det låter dig iterera över objekt och arrayer. Syntaxen för each()-metoden ser ut så här:

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

I kodblocket ovan körs callback för varje iteration av array- eller objektargumentet.

Laddar HTML med Cheerio

För att börja tolka HTML- eller XML-data med Cheerio kan du använda metoden cheerio.load() . Ta en titt på detta exempel:

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

Detta kodblock använder metoden jQuery text() hämtar textinnehållet i h1-elementet. Den fullständiga syntaxen för metoden load() ser ut så här:

 load(content, options, mode)

Content-parametern hänvisar till den faktiska HTML- eller XML-data som du skickar load()-metoden. options är ett valfritt objekt som kan ändra metodens beteende. Som standard introducerar metoden load() html-, head- och body-element om de saknas. Om du vill stoppa detta beteende, se till att du ställer in läget till false.

Skrapa hackernyheter med Cheerio

Koden som används i detta projekt är tillgänglig i en GitHub-förråd och är gratis för dig att använda under MIT-licensen.

Det är dags att kombinera allt du har lärt dig hittills och skapa en enkel webbskrapa. Hacker News är en populär webbplats för entreprenörer och innovatörer. Det är också en perfekt webbplats att utnyttja dina webbskrapningsfärdigheter på eftersom den laddas snabbt, har ett mycket enkelt gränssnitt och inte visar några annonser.

Se till att du har Node.js och Node Package Manager igång på din dator. Skapa en tom mapp, sedan en package.json-fil och lägg till följande JSON i filen:

 {
  "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"
  }
}

Efter att ha gjort det, öppna terminalen och kör:

 npm i

Detta bör installera de nödvändiga beroenden du behöver för att bygga skrapan. Dessa paket inkluderar Cheerio för att analysera HTML, ExpressJS för att skapa servern och – som ett utvecklingsberoende – Nodemon, ett verktyg som lyssnar efter ändringar i projektet och automatiskt startar om servern.

Ställa in saker och skapa de nödvändiga funktionerna

Skapa en index.js-fil och i den filen, skapa en konstant variabel som heter ”PORT”. Ställ in PORT till 5500 (eller vilket nummer du än väljer), importera sedan Cheerio- och Express-paketen.

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

Definiera sedan tre variabler: url, html och finishedPage. Ställ in webbadressen till Hacker News URL.

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

Skapa nu en funktion som heter getHeader() som returnerar lite HTML som webbläsaren ska rendera.

 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>
`}

Skapa en annan funktion getScript() som returnerar lite JavaScript för webbläsaren att köra. Se till att du skickar in variabeltypen som ett argument när du anropar den.

 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>`
}

Slutligen, skapa en asynkron funktion som heter fetchAndRenderPage(). Den här funktionen gör precis vad du tror – den skrapar en sida i Hacker News, analyserar och formaterar den med Cheerio, och skickar sedan lite HTML tillbaka till klienten för rendering.

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

På Hacker News finns det olika typer av inlägg tillgängliga. Det finns ”nyheter”, som är grejen på förstasidan, inlägg som söker svar från andra Hacker News-medlemmar har etiketten ”fråga”. Trendinlägg har etiketten ”bäst”, de senaste inläggen har etiketten ”nyaste” och inlägg om lediga jobb har etiketten ”jobb”.

fetchAndRenderPage() hämtar listan med inlägg från sidan Hacker News baserat på typen du skickar in som argument. Om hämtningsoperationen lyckas binder funktionen html-variabeln till svarstexten.

Lägg sedan till följande rader till funktionen:

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

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

I kodblocket ovan ställer metoden set() in HTTP-huvudfältet. Write()-metoden är ansvarig för att skicka en del av svarskroppen. Funktionen load() tar in html som ett argument.

Lägg sedan till följande rader för att välja respektive barn till alla element med klassen ”titelrad”.

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

I detta kodblock hämtar varje iteration textinnehållet i HTML-målelementet och lagrar det i titelvariabeln.

Tryck sedan in svaret från getScript()-funktionen till artikelmatrisen. Skapa sedan en variabel, finishedPage, som innehåller den färdiga HTML-koden för att skicka till webbläsaren. Använd slutligen metoden write() för att skicka finishedPage som en bit och avsluta svarsprocessen med metoden end().

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

Definiera rutter för att hantera GET-förfrågningar

Precis under fetchAndRenderPage-funktionen, använd metoden express get() för att definiera respektive rutter för olika typer av inlägg. Använd sedan lyssnametoden för att lyssna efter anslutningar till den angivna porten på ditt lokala nätverk.

 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)

I kodblocket ovan har varje get-metod en callback-funktion som anropar fetchAndRenderPage-funktionen som skickar in respektive typer och res-objekten.

När du öppnar din terminal och kör npm kör start. Servern bör starta, sedan kan du besöka localhost:5500 i din webbläsare för att se resultatet.

Grattis, du lyckades precis skrapa Hacker News och hämta inläggets titlar utan behov av ett externt API.

Ta saker vidare med webbskrapning

Med data du skrapar från Hacker News kan du skapa olika visualiseringar som diagram, grafer och ordmoln för att presentera insikter och trender i ett mer lättsmält format.

Du kan också skrapa användarprofiler för att analysera användarnas rykte på plattformen baserat på faktorer som mottagna uppröster, gjorda kommentarer och mer.