Uma introdução ao Web Scraping com o Cheerio

Web scraping é uma técnica que possibilita a obtenção de dados de um determinado site. Os sites usam HTML para descrever seu conteúdo. Se o HTML for limpo e semântico, será fácil usá-lo para localizar dados úteis.

Normalmente, você usará um web scraper para obter e monitorar dados e rastrear futuras alterações neles.

Conceitos de jQuery que vale a pena conhecer antes de usar o Cheerio

jQuery é um dos pacotes JavaScript mais populares existentes. Isso torna mais fácil trabalhar com o Document Object Model (DOM), lidar com eventos, animações e muito mais. Cheerio é um pacote para web scraping que se baseia em jQuery – compartilhando a mesma sintaxe e API, ao mesmo tempo em que facilita a análise de documentos HTML ou XML.

Antes de aprender a usar Cheerio, é importante saber como selecionar elementos HTML com jQuery. Felizmente, o jQuery suporta a maioria dos seletores CSS3, o que facilita a captura de elementos do DOM. Dê uma olhada no seguinte código:

 $("#container");

No bloco de código acima, o jQuery seleciona os elementos com o id de “container”. Uma implementação semelhante usando JavaScript antigo regular seria algo como isto:

 document.querySelectorAll("#container");

Comparando os dois últimos blocos de código, você pode ver que o primeiro bloco de código é muito mais fácil de ler do que o último. Essa é a beleza do jQuery.

jQuery também possui métodos úteis como text(), html() e outros que possibilitam a manipulação de elementos HTML. Existem vários métodos que você pode usar para percorrer o DOM, como parent(), brothers(), prev() e next().

O método each() em jQuery é muito popular em muitos projetos Cheerio. Ele permite iterar sobre objetos e arrays. A sintaxe para o método each() se parece com isto:

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

No bloco de código acima, o retorno de chamada é executado para cada iteração da matriz ou argumento do objeto.

  Ocultar / Reexibir ícones da área de trabalho no OS X com comando de terminal

Carregando HTML com Cheerio

Para começar a analisar dados HTML ou XML com o Cheerio, você pode usar o método cheerio.load(). Dê uma olhada neste exemplo:

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

Este bloco de código usa o método jQuery text() recupera o conteúdo de texto do elemento h1. A sintaxe completa para o método load() se parece com isto:

 load(content, options, mode)

O parâmetro content refere-se aos dados HTML ou XML reais que você passa ao método load(). opções é um objeto opcional que pode modificar o comportamento do método. Por padrão, o método load() introduz os elementos html, head e body se estiverem ausentes. Se você deseja interromper esse comportamento, certifique-se de definir o modo como falso.

Raspando notícias de hackers com Cheerio

O código utilizado neste projeto está disponível em um Repositório GitHub e é gratuito para você usar sob a licença do MIT.

É hora de combinar tudo o que você aprendeu até agora e criar um web scraper simples. Hacker News é um site popular para empreendedores e inovadores. Também é um site perfeito para aproveitar suas habilidades de raspagem na web porque carrega rápido, tem uma interface muito simples e não exibe nenhum anúncio.

Certifique-se de ter o Node.js e o Node Package Manager em execução em sua máquina. Crie uma pasta vazia, depois um arquivo package.json e adicione o seguinte JSON dentro do arquivo:

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

Depois de fazer isso, abra o terminal e execute:

 npm i

Isso deve instalar as dependências necessárias para construir o scraper. Esses pacotes incluem o Cheerio para analisar o HTML, o ExpressJS para criar o servidor e, como uma dependência de desenvolvimento, o Nodemon, um utilitário que detecta alterações no projeto e reinicia automaticamente o servidor.

Configurando as coisas e criando as funções necessárias

Crie um arquivo index.js e, nesse arquivo, crie uma variável constante chamada “PORT”. Defina PORT para 5500 (ou qualquer número que você escolher) e importe os pacotes Cheerio e Express, respectivamente.

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

Em seguida, defina três variáveis: url, html e finishPage. Defina url como o URL do Hacker News.

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

Agora crie uma função chamada getHeader() que retorna algum HTML que o navegador deve renderizar.

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

Crie outra função getScript() que retorne algum JavaScript para o navegador executar. Certifique-se de passar o tipo de variável como um argumento ao chamá-lo.

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

Finalmente, crie uma função assíncrona chamada fetchAndRenderPage(). Essa função faz exatamente o que você pensa – ela extrai uma página no Hacker News, analisa e formata com o Cheerio e, em seguida, envia algum HTML de volta ao cliente para renderização.

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

No Hacker News, existem diferentes tipos de postagens disponíveis. Há as “notícias”, que são as coisas da primeira página, as postagens que buscam respostas de outros membros do Hacker News têm o rótulo “pergunte”. Os posts de tendências têm o rótulo “melhor”, os posts mais recentes têm o rótulo “mais novo” e os posts sobre vagas de emprego têm o rótulo “empregos”.

  Restaure a percepção do som com estes 11 aplicativos de aparelhos auditivos

fetchAndRenderPage() busca a lista de postagens da página Hacker News com base no tipo que você passa como argumento. Se a operação de busca for bem-sucedida, a função vinculará a variável html ao texto de resposta.

Em seguida, adicione as seguintes linhas à função:

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

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

No bloco de código acima, o método set() define o campo de cabeçalho HTTP. O método write() é responsável por enviar um pedaço do corpo da resposta. A função load() aceita html como um argumento.

Em seguida, adicione as seguintes linhas para selecionar os respectivos filhos de todos os elementos com a classe “titleline”.

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

Nesse bloco de código, cada iteração recupera o conteúdo de texto do elemento HTML de destino e o armazena na variável de título.

  Corrigir o erro 1500 Outra instalação está em andamento

Em seguida, envie a resposta da função getScript() para a matriz de artigos. Em seguida, crie uma variável, finishPage, que conterá o HTML finalizado para enviar ao navegador. Por fim, use o método write() para enviar a FinishPage como um bloco e finalize o processo de resposta com o método end().

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

Definindo as rotas para lidar com solicitações GET

Logo abaixo da função fetchAndRenderPage, use o método express get() para definir as respectivas rotas para diferentes tipos de posts. Em seguida, use o método listen para escutar as conexões com a porta especificada em sua rede local.

 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)

No bloco de código acima, todo método get tem uma função de retorno de chamada que chama a função fetchAndRenderPage passando os respectivos tipos e os objetos res.

Quando você abre seu terminal e executa npm run start. O servidor deve iniciar, então você pode visitar localhost:5500 em seu navegador para ver os resultados.

Parabéns, você acabou de raspar o Hacker News e buscar os títulos dos posts sem a necessidade de uma API externa.

Levando as coisas adiante com a raspagem da Web

Com os dados que você extrai do Hacker News, você pode criar várias visualizações, como tabelas, gráficos e nuvens de palavras para apresentar insights e tendências em um formato mais digerível.

Você também pode coletar perfis de usuários para analisar a reputação dos usuários na plataforma com base em fatores como votos positivos recebidos, comentários feitos e muito mais.