Scraping com Cheerio: Como recolher facilmente dados de páginas Web

Raluca Penciuc em 21 de dezembro de 2022

Longe vão os dias em que recolhia e processava manualmente os dados que o ajudavam a dar início aos seus projectos. Quer se trate de um sítio Web de comércio eletrónico ou de um algoritmo de geração de leads, uma coisa é certa: o processo de recolha de dados era fastidioso e moroso.

Neste artigo, ficará a saber como o Cheerio o pode ajudar com as suas extensas funcionalidades de análise de linguagens de marcação, primeiro com alguns exemplos triviais e depois com um caso de utilização real.

Introdução ao Cheerio

"Mas o que é o Cheerio?", pode perguntar-se. Bem, para tentar esclarecer um equívoco comum, vou começar pelo que o Cheerio não é: um navegador.

A confusão pode começar pelo facto de o Cheerio analisar documentos escritos numa linguagem de marcação e, em seguida, oferecer uma API para o ajudar a manipular a estrutura de dados resultante. Mas, ao contrário de um navegador, o Cheerio não renderiza visualmente o documento, não carrega arquivos CSS nem executa Javascript.

Basicamente, o que o Cheerio faz é receber uma entrada HTML ou XML, analisar a cadeia de caracteres e devolver a API. Isto torna-o incrivelmente rápido e fácil de utilizar, daí a sua popularidade entre os programadores de Node.js.

Configurar o ambiente

Agora, vamos ver alguns exemplos práticos do que o Cheerio pode fazer. Antes de mais nada, é preciso ter certeza de que seu ambiente está configurado.

Escusado será dizer que tem de ter o Node.js instalado na sua máquina. Se não tiver, basta seguir as instruções do site oficial, de acordo com o seu sistema operativo.

Certifique-se de baixar a versão Long Term Support (LTS) e não se esqueça do Node.js Package Manager (NPM). Pode executar estes comandos para se certificar de que a instalação correu bem:

node -v
npm -v

O resultado deve ter o seguinte aspeto:

imagem do blogue

Agora, relativamente ao debate sobre o IDE: para este tutorial, vou utilizar o Visual Studio Code, uma vez que é bastante flexível e fácil de utilizar, mas pode utilizar qualquer IDE que preferir.

Basta criar uma pasta que irá conter o seu pequeno projeto e abrir um terminal. Execute o seguinte comando para configurar um projeto Node.js:

npm init -y

Isso criará uma versão padrão do arquivo package.json, que pode ser modificado a qualquer momento.

Próximo passo: Vou instalar o TypeScript junto com as definições de tipo para o Node.js:

npm install typescript @types/node -save-dev

Escolhi o TypeScript neste tutorial devido à sua tipagem estática opcional para objectos JavaScript, o que torna o código mais resistente a erros de tipo. 

Esta é a mesma vantagem que aumentou constantemente a sua popularidade entre a comunidade JavaScript, de acordo com um inquérito recente da CircleCI sobre as linguagens de programação mais populares.

Para verificar a instalação correta do comando anterior, pode executar:

npx tsc --versão

Agora vou criar o arquivo de configuração tsconfig.json na raiz do diretório do projeto, que deve definir as opções do compilador. Se quiser entender melhor esse arquivo e suas propriedades, a documentação oficial do TypeScript está à sua disposição. 

Caso contrário, basta copiar e colar o seguinte:

{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"lib": ["es2015"]
}

Quase pronto! Agora tens de instalar o Cheerio (obviamente):

npm install cheerio

Por último, mas não menos importante, crie o diretório src que irá conter os ficheiros de código. E por falar em ficheiros de código, crie e coloque o ficheiro index.ts na diretoria src .

Como é que o Cheerio funciona

Perfeito! Agora podes começar.

Para já, vou ilustrar algumas funcionalidades básicas do Cheerio utilizando um documento HTML estático. Basta copiar e colar o conteúdo abaixo em um novo arquivo static.html dentro do seu projeto:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Page Name - Static HTML Example</title>
</head>
<body>
<div class="page-heading">
<h1>Page Heading</h1>
</div>
<div class="page-container">
<div class="page-content">
<ul>
<li>
<a href="#">Item 1</a>
<p class="price">$100</p>
<p class="stock">12</p>
</li>
<li>
<a href="#">Item 2</a>
<p class="price">$200</p>
<p class="stock">422</p>
</li>
<li>
<a href="#">Item 3</a>
<p class="price">$150</p>
<p class="stock">5</p>
</li>
</ul>
</div>
</div>
<footer class="page-footer">
<p>Last Updated: Friday, September 23, 2022</p>
</footer>
</body>
</html>

Em seguida, é necessário fornecer o ficheiro HTML como entrada ao Cheerio, que devolverá a API resultante:

import fs from 'fs'
import * as cheerio from 'cheerio'

const staticHTML = fs.readFileSync('static.html')
const $ = cheerio.load(staticHTML)

Se receber um erro nesta etapa, certifique-se de que o ficheiro de entrada contém um documento HTML válido, uma vez que a partir da versão 1.0.0 do Cheerio este critério também é verificado.

Agora pode começar a experimentar o que o Cheerio tem para oferecer. O pacote NPM é conhecido pela sua sintaxe semelhante à do jQuery e pela utilização de selectores CSS para extrair os nós que procura. Pode consultar a sua documentação oficial para ficar com uma ideia melhor.

Digamos que pretende extrair o título da página:

const title = $('title').text()
console.log("Título da página HTML estática:", title)

Devíamos testar isto, certo? Está a usar Typescript, por isso tem de compilar o código, que irá criar o diretório dist , e depois executar o ficheiro index.js associado. Para simplificar, vou definir o seguinte script no ficheiro package.json:

"scripts": {
"test": "npx tsc && node dist/index.js",
}

Desta forma, tudo o que tenho de fazer é correr:

npm run test

e o guião tratará de ambos os passos que acabei de descrever.

Está bem, mas e se o seletor corresponder a mais do que um elemento HTML? Vamos tentar extrair o nome e o valor do stock dos itens apresentados na lista não ordenada:

const itemStocks = {}
$('li').each((index, element) => {
const name = $(element).find('a').text()
const stock = $(element).find('p.stock').text()
itemStocks[name] = stock
})
console.log("All items stock:", itemStocks)

Agora, execute o script de atalho novamente, e a saída do seu terminal deve ser semelhante a esta:

Static HTML page title: Page Name - Static HTML Example
All items stock: { 'Item 1': '12', 'Item 2': '422', 'Item 3': '5' }

Casos de utilização do Cheerio

Isto foi basicamente a ponta do icebergue. O Cheerio também é capaz de analisar documentos XML, extrair o estilo dos elementos HTML e até alterar os atributos dos nós.

Mas como é que o Cheerio pode ajudar num caso de utilização real?

Digamos que queremos reunir alguns dados para treinar um modelo de aprendizagem automática para um projeto maior no futuro. Normalmente, procuramos no Google alguns ficheiros de treino e descarregamo-los, ou utilizamos a API do sítio Web.

Mas o que fazer quando não se consegue encontrar alguns ficheiros relevantes ou o sítio Web que se está a consultar não fornece uma API, tem um limite de taxa sobre os dados ou não oferece todos os dados que se vê numa página?

Bem, é aqui que a recolha de dados da Web é útil. Se tem curiosidade em conhecer casos de utilização mais práticos da recolha de dados da Web, pode consultar este artigo bem escrito do nosso blogue.

Voltando à nossa ovelha, para efeitos do exemplo, vamos considerar que estamos nesta situação exacta: queremos dados e os dados não estão em lado nenhum. Lembre-se que o Cheerio não trata da extração de HTML, nem do carregamento de CSS ou da execução de JS.

Assim, no nosso tutorial, estou a utilizar o Puppeteer para navegar para o Web site, obter o HTML e guardá-lo num ficheiro. Depois, vou repetir o processo da secção anterior.

Para ser mais específico, quero reunir algumas opiniões públicas do Reddit sobre um módulo de bateria popular e centralizar os dados num único ficheiro que será posteriormente alimentado a um potencial modelo de treino de ML. O que acontece a seguir também pode variar: análise de sentimentos, estudos de mercado e a lista pode continuar.

imagem do blogue

Pedido de HTML

Vamos ver como esse caso de uso será colocado no código. Primeiro, é necessário instalar o pacote NPM do Puppeteer:

npm install puppeteer

Vou também criar um novo ficheiro reddit.ts, para manter o projeto mais organizado, e definir um novo script no ficheiro package.json:

"scripts": {
"test": "npx tsc && node dist/index.js",
"parse": "npx tsc && node dist/reddit.js"
},

Para obter o documento HTML, vou definir uma função com o seguinte aspeto:

import fs from 'fs'
import puppeteer from 'puppeteer'
import * as cheerio from 'cheerio'

async function getContent(url: string): Promise<void> {

// Open the browser and a new tab
const browser = await puppeteer.launch()
const page = await browser.newPage()

// Navigate to the URL and write the content to file
await page.goto(url)
const pageContent = await page.content()
fs.writeFileSync("reddit.html", pageContent)

// Close the browser
await browser.close()
console.log("Got the HTML. Check the reddit.html file.")
}

Para testar isto rapidamente, adicione um ponto de entrada no seu código e chame a função:

async function main() {


const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
await getContent(targetURL)
}

main()
.then(() => {console.log("All done!")})
.catch(e => {console.log("Unexpected error occurred:", e.message)})

O ficheiro reddit.html deve aparecer na sua árvore de projectos, que conterá o documento HTML que pretendemos.

Onde estão os meus nódulos?

Agora, uma parte um pouco mais difícil: tem de identificar os nós que são de interesse para o nosso caso de utilização. Volte ao seu browser (o verdadeiro) e navegue até ao URL de destino. Coloque o cursor do rato sobre a secção de comentários, clique com o botão direito do rato e escolha a opção "Inspecionar".

O separador Ferramentas de desenvolvimento será aberto, mostrando-lhe exatamente o mesmo documento HTML que guardou anteriormente no seu computador.

imagem do blogue

Para extrair apenas os comentários, é necessário identificar os selectores exclusivos desta secção da página. Pode reparar que toda a lista de comentários está dentro de um contentor div com uma classe sitetable nestedlisting.

Indo mais fundo, cada comentário individual tem um elemento de formulário como pai, com a classe usertext warn-on-unload. Depois, na parte inferior, pode ver que o texto de cada comentário está dividido entre vários elementos p.

Analisar e guardar os dados

Vamos ver como isto funciona em código:

function parseComments(): void {

// Load the HTML document
const staticHTML = fs.readFileSync('reddit.html')
const $ = cheerio.load(staticHTML)

// Get the comments section
const commentsSection = $('div.sitetable.nestedlisting')

// Iterate each comment
const comments = []


$(commentsSection).find('form.usertext.warn-on-unload').each((index, comment) => {
let commentText = ""

// Iterate each comment section and concatenate them
$(comment).find('p').each((index, piece) => {
commentText += $(piece).text() + '\n'
})

comments.push(commentText)
})

// Write the results to external file
fs.writeFileSync("comments.json", JSON.stringify({comments}))
}

Muito bem, agora vamos atualizar o ponto de entrada com a nova função definida e ver como este código está a funcionar em conjunto:

async function main() {

const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
await getContent(targetURL)
parseComments()
}

main()
.then(() => {console.log("All done. Check the comments.csv file.")})
.catch(e => {console.log("Unexpected error occurred:", e.message)})

Executar o código com o script definido anteriormente:

npm run parse

Levará cerca de 5 a 10 segundos para o navegador sem cabeça abrir e navegar para o nosso URL de destino. Se estiveres mais curioso, podes adicionar marcas de tempo no início e no fim de cada uma das nossas funções para veres realmente quão rápido é o Cheerio.

O ficheiro comments.json deve ser o nosso resultado final:

imagem do blogue

Este caso de utilização pode ser facilmente alargado para analisar o número de votos positivos e negativos de cada comentário, ou para obter as respostas aninhadas dos comentários. As possibilidades são infinitas.

Para levar

Obrigado por ter chegado ao fim deste tutorial. Espero que tenha percebido como o Cheerio é indispensável para o processo de extração de dados e como integrá-lo rapidamente no seu próximo projeto de scraping.

Também estamos a utilizar o Cheerio no nosso produto, WebScrapingAPI. Se alguma vez se sentir confuso com os muitos desafios encontrados na recolha de dados da Web (bloqueios de IP, deteção de bots, etc.), considere experimentar o Cheerio.

Notícias e actualizações

Mantenha-se atualizado com os mais recentes guias e notícias sobre raspagem da Web, subscrevendo a nossa newsletter.

We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artigos relacionados

miniatura
GuiasGuia de início rápido da API de raspagem da Web

Comece a utilizar o WebScrapingAPI, a derradeira solução de raspagem da Web! Recolha dados em tempo real, contorne sistemas anti-bot e beneficie de apoio profissional.

Mihnea-Octavian Manolache
avatar do autor
Mihnea-Octavian Manolache
9 min. de leitura
miniatura
Ciência da recolha de dados da WebWeb Scraping facilitado: a importância da análise de dados

Descubra como extrair e organizar eficientemente dados para raspagem da Web e análise de dados através de análise de dados, bibliotecas de análise de HTML e metadados schema.org.

Suciu Dan
avatar do autor
Suciu Dan
12 min ler
miniatura
GuiasComo construir um raspador e descarregar um ficheiro com o Puppeteer

Descubra 3 formas de descarregar ficheiros com o Puppeteer e construa um web scraper que faz exatamente isso.

Mihnea-Octavian Manolache
avatar do autor
Mihnea-Octavian Manolache
8 min. de leitura