A técnica de web scraping possibilita a extração de dados de páginas web. Sites utilizam HTML para estruturar seu conteúdo, e quando este HTML é bem construído e semântico, a localização de informações relevantes torna-se facilitada.
Geralmente, o web scraping é empregado para coletar, acompanhar informações e monitorar alterações em dados específicos.
Conceitos de jQuery Essenciais Antes de Usar Cheerio
jQuery é uma biblioteca JavaScript amplamente utilizada, que simplifica a interação com o Document Object Model (DOM), o tratamento de eventos e a criação de animações. Cheerio, por sua vez, é uma biblioteca para web scraping que se inspira no jQuery, compartilhando sua sintaxe e API, e facilita a análise de documentos HTML ou XML.
Antes de usar o Cheerio, é fundamental compreender como selecionar elementos HTML com jQuery. jQuery oferece suporte à maioria dos seletores CSS3, o que simplifica a identificação de elementos no DOM. Veja o exemplo:
$("#container");
Neste trecho de código, jQuery seleciona elementos que possuem o ID “container”. Uma implementação equivalente com JavaScript puro seria:
document.querySelectorAll("#container");
A comparação dos dois exemplos evidencia que o código jQuery é mais legível e conciso, ilustrando a vantagem de seu uso.
jQuery também inclui métodos úteis como `text()`, `html()`, que permitem manipular elementos HTML. Há também diversos métodos para navegar pelo DOM, como `parent()`, `siblings()`, `prev()` e `next()`.
O método `each()` do jQuery é muito usado em projetos que utilizam Cheerio, permitindo iterar sobre objetos e arrays. A sintaxe do `each()` é:
$(<element>).each(<array or object>, callback)
Nesse código, a função de callback é executada em cada iteração do array ou objeto fornecido.
Carregando HTML com Cheerio
Para começar a análise de dados HTML ou XML com Cheerio, use o método `cheerio.load()`. Observe este exemplo:
const $ = cheerio.load('<html><body><h1>Olá, mundo!</h1></body></html>');
console.log($('h1').text())
Este código utiliza o método `text()` do jQuery para obter o texto do elemento h1. A sintaxe completa do método `load()` é:
load(content, options, mode)
O parâmetro `content` representa os dados HTML ou XML. `options` é um objeto opcional que pode modificar o comportamento do método. Por padrão, o `load()` insere os elementos `html`, `head` e `body` caso estejam ausentes. Para evitar este comportamento, o modo deve ser definido como `false`.
Extraindo Notícias do Hacker News com Cheerio
O código deste projeto está disponível em um repositório GitHub, sob licença MIT.
Vamos combinar os conhecimentos adquiridos para criar um web scraper simples. O Hacker News é um site popular entre empreendedores e inovadores, e ideal para a prática de web scraping, pois carrega rapidamente, possui uma interface simples e não exibe publicidade.
Certifique-se de ter o Node.js e o Node Package Manager instalados. Crie uma pasta vazia, um arquivo `package.json` e insira o seguinte 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"
}
}
Em seguida, abra o terminal e execute:
npm i
Isso instalará as dependências necessárias para construir o scraper, incluindo Cheerio para análise de HTML, ExpressJS para criar o servidor e Nodemon como dependência de desenvolvimento para detectar alterações e reiniciar o servidor.
Configurando e Criando as Funções Necessárias
Crie um arquivo `index.js` e declare uma constante `PORT`, definindo-a para 5500 (ou outro número) e importe os pacotes Cheerio e Express:
const PORT = 5500;
const cheerio = require("cheerio");
const express = require("express");
const app = express();
Em seguida, declare três variáveis: `url`, `html` e `finishedPage`. Defina `url` como o endereço do Hacker News:
const url="https://news.ycombinator.com";
let html;
let finishedPage;
Agora, crie uma função `getHeader()` que retorna um HTML a ser renderizado no navegador:
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 retorna JavaScript para o navegador executar, passando o tipo da variável como argumento ao chamá-la:
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>`
}
Por fim, crie uma função assíncrona `fetchAndRenderPage()`. Esta função busca uma página do Hacker News, a analisa com Cheerio e envia o HTML para o cliente:
async function fetchAndRenderPage(type, res) {
const response = await fetch(`${url}${type}`)
html = await response.text();
}
O Hacker News oferece diferentes tipos de posts, como “notícias” da página inicial, posts para perguntas (“ask”), posts de tendências (“best”), posts mais recentes (“newest”) e posts de vagas de emprego (“jobs”).
`fetchAndRenderPage()` busca a lista de posts de acordo com o tipo fornecido. Se a busca for bem-sucedida, a função associa 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;
Aqui, o método `set()` define o cabeçalho HTTP e o método `write()` envia uma parte do corpo da resposta. A função `load()` recebe o `html` como argumento.
Em seguida, adicione estas linhas para selecionar os 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++;
})
Neste trecho, a cada iteração, o conteúdo de texto do elemento HTML é recuperado e armazenado na variável `title`.
Em seguida, inclua a resposta da função `getScript()` na matriz `articles`. Crie a variável `finishPage` que conterá o HTML final a ser enviado ao navegador e utilize o método `write()` para enviar `finishPage` e finalizar o processo de resposta com `end()`:
articles.push(getScript(type))
finishedPage = articles.reduce((c, n) => c + n);
res.write(finishedPage);
res.end();
Definindo as Rotas para Solicitações GET
Abaixo da função `fetchAndRenderPage`, utilize o método `express get()` para definir as rotas para os diferentes tipos de posts. Em seguida, utilize o método `listen` para monitorar conexões na 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)
Cada método `get` tem uma função de retorno que chama `fetchAndRenderPage`, passando o tipo de postagem e o objeto `res`.
Ao executar `npm run start` no terminal, o servidor iniciará e você poderá acessar `localhost:5500` no navegador para ver os resultados.
Parabéns, você acabou de extrair os títulos do Hacker News sem a necessidade de uma API externa.
Avançando com Web Scraping
Com os dados extraídos do Hacker News, é possível criar diversas visualizações, como tabelas, gráficos e nuvens de palavras, para apresentar insights e tendências de forma mais acessível.
Também é possível coletar informações de perfis de usuários para analisar sua reputação na plataforma, com base em fatores como votos recebidos, comentários e outros dados relevantes.