Nettskraping med Cheerio: Guide til å hente data fra nettsider

Webskraping er en metode for å hente data fra spesifikke nettsider. Nettsider bruker HTML for å definere innholdet. Hvis HTML-koden er ryddig og semantisk, blir det enklere å finne relevant informasjon.

En webskraper brukes vanligvis for å samle inn, overvåke data og registrere eventuelle fremtidige endringer.

jQuery-prinsipper det er viktig å kjenne til før Cheerio tas i bruk

jQuery er en av de mest brukte JavaScript-pakkene. Den forenkler arbeidet med Document Object Model (DOM), håndtering av hendelser, animasjoner og mer. Cheerio er en pakke for webskraping som bygger på jQuery – den deler samme syntaks og API, samtidig som den forenkler analysen av HTML- eller XML-dokumenter.

Før man lærer å bruke Cheerio, er det viktig å forstå hvordan man velger HTML-elementer ved hjelp av jQuery. Heldigvis støtter jQuery de fleste CSS3-selektorer, noe som gjør det lettere å hente elementer fra DOM. Se følgende eksempel:

 $("#container");

I kodeblokken over velger jQuery elementene med ID-en «container». En tilsvarende implementering i ren JavaScript vil se omtrent slik ut:

 document.querySelectorAll("#container");

Ved å sammenligne disse to kodeblokkene er det tydelig at den første er mer lesbar. Det er noe av styrken til jQuery.

jQuery har også nyttige metoder som text(), html() med flere, som gjør det mulig å manipulere HTML-elementer. Det finnes flere metoder for å navigere DOM, som parent(), siblings(), prev() og next().

each()-metoden i jQuery er ofte brukt i mange Cheerio-prosjekter. Den lar deg iterere gjennom objekter og matriser. Syntaksen for each()-metoden ser slik ut:

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

I koden over vil tilbakeringingsfunksjonen kjøres for hver iterasjon av matrisen eller objektargumentet.

Laste inn HTML med Cheerio

For å begynne å analysere HTML- eller XML-data med Cheerio, kan du bruke metoden cheerio.load(). Ta en titt på dette eksemplet:

 const $ = cheerio.load('<html><body><h2>Hei, verden!</h2></body></html>');
console.log($('h2').text())

Dette kodeutdraget bruker jQuery-metoden text() til å hente tekstinnholdet til h2-elementet. Den fullstendige syntaksen for load()-metoden ser slik ut:

 load(innhold, alternativer, modus)

Parameteren innhold refererer til selve HTML- eller XML-dataene som sendes til load()-metoden. Alternativer er et valgfritt objekt som kan endre metodens oppførsel. Som standard legger load()-metoden til html-, head- og body-elementer hvis disse mangler. For å stoppe denne oppførselen må du sette modus til usann.

Skrape Hacker News med Cheerio

Koden som brukes i dette eksemplet er tilgjengelig i et GitHub-repositorium, og kan brukes fritt under MIT-lisensen.

Nå er tiden inne for å sette sammen det du har lært og lage en enkel webskraper. Hacker News er et populært nettsted for gründere og innovatører. Det er også et utmerket sted for å øve på webskraping fordi det lastes raskt, har et veldig enkelt grensesnitt og ikke inneholder reklame.

Sørg for at Node.js og Node Package Manager er installert på datamaskinen din. Opprett en tom mappe og deretter en package.json-fil, og legg følgende JSON inn 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"
  }
}

Etter å ha gjort dette, åpne terminalen og kjør:

 npm i

Dette installerer de nødvendige avhengighetene du trenger for å bygge skraperen. Disse pakkene inkluderer Cheerio for å analysere HTML, ExpressJS for å lage serveren, og – som en utviklingsavhengighet – Nodemon, et verktøy som overvåker endringer i prosjektet og automatisk starter serveren på nytt.

Konfigurere og opprette de nødvendige funksjonene

Opprett en fil som heter index.js, og legg til en konstant variabel kalt «PORT» i den filen. Sett PORT til 5500 (eller et annet tall du velger), og importer pakkene Cheerio og Express.

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

Deretter definerer du tre variabler: url, html og finishedPage. Sett url til Hacker News URL.

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

Lag nå en funksjon som heter getHeader() som returnerer litt HTML som nettleseren skal gjengi.

 function getHeader(){
    return `
        <div style="display:flex; flex-direction:column; align-items:center;">
        <h3 style="text-transform:capitalize">Skrapte Nyheter</h3>
        <div style="display:flex; gap:10px; align-items:center;">
        <a href="https://www.makeuseof.com/" id="news" onClick='showLoading()'>Hjem</a>
        <a href="https://wilku.top/best" id="best" onClick='showLoading()'>Beste</a>
        <a href="https://wilku.top/newest" id="newest" onClick='showLoading()'>Nyeste</a>
        <a href="https://wilku.top/ask" id="ask" onClick='showLoading()'>Spør</a>
        <a href="https://wilku.top/jobs" id="jobs" onClick='showLoading()'>Jobber</a>
        </div>
        <p class="loading" style="display:none;">Laster...</p>
        </div>
`}

Lag en annen funksjon getScript() som returnerer JavaScript-kode som skal kjøres av nettleseren. Sørg for at du sender inn variabeltypen som et argument når du kaller 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>`
}

Til slutt lager du en asynkron funksjon som heter fetchAndRenderPage(). Denne funksjonen gjør det du tror – den skraper en side fra Hacker News, analyserer og formaterer den med Cheerio, og sender deretter litt HTML tilbake til klienten for gjengivelse.

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

Hacker News har forskjellige typer innlegg tilgjengelig. Det er «nyhetene», som er det som vises på forsiden, innlegg som søker svar fra andre Hacker News-medlemmer er merket med «spør». Trendende innlegg er merket med «beste», de siste innleggene er merket med «nyeste», og innlegg om ledige stillinger er merket med «jobber».

fetchAndRenderPage() henter listen over innlegg fra Hacker News-siden basert på typen du sender inn som argument. Hvis hentingen er vellykket, tilordnes svarteksten til variabelen html.

Deretter legger du til disse linjene i funksjonen:

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

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

I koden over setter set()-metoden HTTP-headerfeltet. Write()-metoden er ansvarlig for å sende en del av svarteksten. load()-funksjonen tar inn html som et argument.

Deretter legger du til følgende linjer for å velge de respektive barna til alle elementene med klassen «tittelrad».

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

I denne koden hentes tekstinnholdet for hvert HTML-element, og lagres i variabelen title.

Deretter legger du til svaret fra getScript()-funksjonen i artikkelmatrisen. Deretter oppretter du en variabel, finishedPage, som skal inneholde den ferdige HTML-koden for å sende til nettleseren. Til slutt bruker du write()-metoden for å sende finishedPage som en del, og avslutter svarprosessen med end()-metoden.

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

Definere rutene for å håndtere GET-forespørsler

Rett under fetchAndRenderPage-funksjonen bruker du express sin get()-metode for å definere de relevante rutene for de forskjellige typer innlegg. Deretter bruker du lyttemetoden for å lytte etter tilkoblinger til den angitte porten på det lokale nettverket.

 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 koden ovenfor har hver get-metode en tilbakeringingsfunksjon som kaller fetchAndRenderPage-funksjonen med de aktuelle typene og res-objektene.

Når du åpner terminalen og kjører npm start, skal serveren starte opp. Deretter kan du gå til localhost:5500 i nettleseren for å se resultatet.

Gratulerer, du har akkurat skrapt Hacker News og hentet posttitlene uten behov for et eksternt API.

Ta webskraping videre

Med dataene du skraper fra Hacker News, kan du lage ulike visualiseringer som diagrammer, grafer og ordskyer for å presentere innsikt og trender i et mer lettfattelig format.

Du kan også skrape brukerprofiler for å analysere omdømmet til brukere på plattformen basert på faktorer som antall oppstemmer, kommentarer med mer.