En introduktion til web-skrabning med Cheerio

Web scraping er en teknik, der gør det muligt at indhente data fra en bestemt hjemmeside. Websites bruger HTML til at beskrive deres indhold. Hvis HTML’en er ren og semantisk, er det let at bruge den til at finde nyttige data.

Du vil typisk bruge en webskraber til at indhente og overvåge data og spore fremtidige ændringer af dem.

jQuery-koncepter, der er værd at vide, før du bruger Cheerio

jQuery er en af ​​de mest populære JavaScript-pakker, der findes. Det gør det nemmere at arbejde med Document Object Model (DOM), håndtere begivenheder, animationer og meget mere. Cheerio er en pakke til web-skrabning, der bygger oven på jQuery – deler den samme syntaks og API, samtidig med at det gør det nemmere at parse HTML- eller XML-dokumenter.

Før du lærer at bruge Cheerio, er det vigtigt at vide, hvordan du vælger HTML-elementer med jQuery. Heldigvis understøtter jQuery de fleste CSS3-vælgere, hvilket gør det lettere at få fat i elementer fra DOM. Tag et kig på følgende kode:

 $("#container");

I kodeblokken ovenfor vælger jQuery elementerne med id’et “container”. En lignende implementering ved hjælp af almindelig gammel JavaScript ville se sådan ud:

 document.querySelectorAll("#container");

Ved at sammenligne de sidste to kodeblokke kan du se, at den førstnævnte kodeblok er meget nemmere at læse end sidstnævnte. Det er det smukke ved jQuery.

jQuery har også nyttige metoder som text(), html() og mere, der gør det muligt at manipulere HTML-elementer. Der er flere metoder, du kan bruge til at krydse DOM’et, som forælder(), søskende(), prev() og next().

Metoden each() i jQuery er meget populær i mange Cheerio-projekter. Det giver dig mulighed for at iterere over objekter og arrays. Syntaksen for each()-metoden ser sådan ud:

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

I kodeblokken ovenfor kører tilbagekald for hver iteration af array- eller objektargumentet.

Indlæser HTML med Cheerio

For at begynde at parse HTML- eller XML-data med Cheerio kan du bruge cheerio.load()-metoden. Tag et kig på dette eksempel:

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

Denne kodeblok bruger jQuery text() metoden henter tekstindholdet i h1-elementet. Den fulde syntaks for load()-metoden ser sådan ud:

 load(content, options, mode)

Indholdsparameteren refererer til de faktiske HTML- eller XML-data, du videregiver load()-metoden. optioner er et valgfrit objekt, der kan ændre metodens adfærd. Som standard introducerer load()-metoden html-, head- og body-elementer, hvis de mangler. Hvis du vil stoppe denne adfærd, skal du sørge for at indstille tilstanden til falsk.

Skrabning af hacker-nyheder med Cheerio

Koden brugt i dette projekt er tilgængelig i en GitHub-depot og er gratis for dig at bruge under MIT-licensen.

Det er tid til at kombinere alt, hvad du har lært indtil nu, og skabe en simpel webskraber. Hacker News er en populær hjemmeside for iværksættere og innovatører. Det er også et perfekt websted at udnytte dine web-skrabefærdigheder på, fordi det indlæses hurtigt, har en meget enkel grænseflade og ikke viser nogen annoncer.

Sørg for, at du har Node.js og Node Package Manager kørende på din maskine. Opret en tom mappe, derefter en package.json-fil, og tilføj følgende 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"
  }
}

Når du har gjort det, skal du åbne terminalen og køre:

 npm i

Dette bør installere de nødvendige afhængigheder, du har brug for til at bygge skraberen. Disse pakker inkluderer Cheerio til at analysere HTML, ExpressJS til at oprette serveren og – som en udviklingsafhængighed – Nodemon, et værktøj, der lytter efter ændringer i projektet og automatisk genstarter serveren.

Opsætning af ting og oprettelse af de nødvendige funktioner

Opret en index.js-fil, og opret en konstant variabel kaldet “PORT” i den fil. Indstil PORT til 5500 (eller hvilket nummer du vælger), og importer derefter henholdsvis Cheerio- og Express-pakkerne.

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

Derefter skal du definere tre variabler: url, html og finishedPage. Indstil url til Hacker News URL.

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

Opret nu en funktion kaldet getHeader(), der returnerer noget HTML, som browseren skal gengive.

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

Opret en anden funktion getScript(), der returnerer noget JavaScript, som browseren kan køre. Sørg for at indtaste variabeltypen som argument, når du kalder 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 sidst skal du oprette en asynkron funktion kaldet fetchAndRenderPage(). Denne funktion gør præcis, hvad du tror – den skraber en side i Hacker News, analyserer og formaterer den med Cheerio og sender derefter noget HTML tilbage til klienten til gengivelse.

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

På Hacker News er der forskellige typer indlæg tilgængelige. Der er “nyhederne”, som er tingene på forsiden, indlæg, der søger svar fra andre Hacker News-medlemmer, har etiketten “spørg”. Trendopslag har mærket “bedst”, de seneste opslag har mærket “nyeste” og opslag vedrørende ledige stillinger har mærket “job”.

fetchAndRenderPage() henter listen over indlæg fra siden Hacker News baseret på den type, du sender ind som argument. Hvis hentehandlingen lykkes, binder funktionen html-variablen til svarteksten.

Tilføj derefter følgende linjer til funktionen:

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

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

I kodeblokken ovenfor angiver set()-metoden HTTP-header-feltet. Write()-metoden er ansvarlig for at sende en del af svarteksten. Funktionen load() tager html ind som et argument.

Tilføj derefter følgende linjer for at vælge de respektive underordnede af alle elementer med klassen “titleline”.

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

I denne kodeblok henter hver iteration tekstindholdet af HTML-målelementet og gemmer det i titelvariablen.

Skub derefter svaret fra getScript()-funktionen ind i artikelarrayet. Opret derefter en variabel, finishedPage, som vil indeholde den færdige HTML til at sende til browseren. Til sidst skal du bruge metoden write() til at sende finishedPage som en chunk og afslutte svarprocessen med end() metoden.

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

Definition af ruter til håndtering af GET-anmodninger

Lige under fetchAndRenderPage-funktionen skal du bruge express get()-metoden til at definere de respektive ruter for forskellige typer indlæg. Brug derefter lyttemetoden til at lytte efter forbindelser til den angivne port på dit lokale netværk.

 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 kodeblokken ovenfor har hver get-metode en tilbagekaldsfunktion, der kalder fetchAndRenderPage-funktionen, der passerer i respektive typer og res-objekterne.

Når du åbner din terminal og kører npm, start start. Serveren skulle starte op, så kan du besøge localhost:5500 i din browser for at se resultaterne.

Tillykke, du nåede lige at skrabe Hacker News og hente posttitlerne uden behov for en ekstern API.

Tag tingene videre med webskrabning

Med de data, du skraber fra Hacker News, kan du oprette forskellige visualiseringer som diagrammer, grafer og ordskyer for at præsentere indsigt og tendenser i et mere fordøjeligt format.

Du kan også skrabe brugerprofiler for at analysere brugernes omdømme på platformen baseret på faktorer som modtaget opstemmer, kommentarer og mere.