Raspagem de dados com NodeJS
Veja como fazer data scraping de um site utilizando NodeJS
Neste tutorial você vai aprender como fazer a raspagem de dados (data scraping) de um site. Como exemplo, vamos pegar os jogos do campeonato brasileiro de 2019, estes dados ficam disponíveis no site da CBF. Para fazer a extração destes dados, vamos utilizar o NodeJS.
Se você não tem o NodeJS instalado em seu computador, pode baixar a partir deste endereço:
https://nodejs.org/
Uma vez instalado, a partir do seu terminal, e dentro da pasta onde você vai guardar o seu projeto, digite
npm init
Você pode responder todas as questões pressionando ENTER. No final do processo, será criado um arquivo chamado package.json, onde serão instaladas as dependências de nosso projeto.
Precisamos instalar duas bibliotecas para o nosso projeto, a primeira será a biblioteca request, ela vai permitir fazer a comunicação com o site e retornar o conteúdo. Para instalar, basta executar o seguinte comando:
npm install request
A segunda biblioteca que vamos instalar é a biblioteca Cheerio. O Cheerio permite manipular a estrutura do HTML, e retornar o conteúdo que desejamos através da utilização de seletores que são semelhantes ao JQuery.
npm install cheerio.
Para saber mais sobre o Cheerio, você pode acessar este link:
https://cheerio.js.org
Crie um arquivo chamado index.js na raiz do seu projeto e adicione o seguinte código.
const request = require("request"),
const cheerio = require("cheerio"),
const url =
"https://www.cbf.com.br/futebol-brasileiro/competicoes/campeonato-brasileiro-serie-a/2019";
request(url, function(error, response, html) {
if (!error) {
console.log(html);
} else {
console.log("Houve um problema ao abrir o endereço informado");
}
});
Para executar, no terminal, digite
node index
A função request vai abrir a URL que informamos. Como ela trabalha de forma assíncrona, ela tem como retorno 3 parâmetros, o erro, o response, e o html, que é o conteúdo. Se não houve erro ao abrir a página, você vai ver na tela o conteúdo que foi recebido. Se tudo deu certo, já podemos começar a programar a leitura dos dados.
Antes de continuar, vamos inspecionar a página para ver o código HTML que foi gerado. Conferindo o código fonte, você vai perceber que os dados estão estruturados da seguinte forma:
<table class="table m-b-20 tabela-expandir">
<thead>
<!--- Linhas de cabeçalho ---->
</thead>
<tbody>
<!--- Linhas de conteúdo ---->
</tbody>
</table>
Vamos fazer um teste, após abrir o arquivo com o Cheerio, vamos fazer um loop em todas as linhas. O código vai ficar da seguinte forma:
request(url, function(error, response, html) {
if (!error) {
//console.log(response);
//console.log(html);
var resultados = [];
var $ = cheerio.load(html);
$(".tabela-expandir")
.find("tr")
.each(function(i) {
console.log('*');
});
}
});
O código acima carrega o HTML do link que foi aberto pela biblioteca request, e criar um objeto chamado "$", que vai ter o conteúdo deste HTML aberto pelo Cheerio. O Cheerio utiliza seletores para manipular o HTML que são parecidos com aqueles utilizados pelo JQuery. O que os seletores fizeram foi o seguinte:
- (".tabela-expandir") - Selecione o elemento com a classe "tabela-expandir"
- .find("tr") - Retorna todos os elementos do tipo TR, que são as linhas da tabela
- .each(function(i) - Para cada linha, faz um loop e executa uma programação a sua escolha.
Se tudo deu certo, você vai ver um asterisco para cada linha da tabela.
Agora precisamos extrair o conteúdo das linhas, mas primeiro, precisamos ver a estrutura de cada uma das linhas. Inspecionando o HTML, vamos tomar como base a primeira linha de conteúdo da tabela.
<tr class="expand-trigger">
<td>
<i class="btn btn-link glyphicon glyphicon-menu-down m-r-10"></i>
<b class="m-l-10 sobe">1º</b>
<span class="color-darkgray m-r-10 m-l-10 small"> 0</span> <img src="https://conteudo.cbf.com.br/cdn/imagens/escudos/00006rj.jpg" title="Flamengo - RJ" alt="Flamengo - RJ" onerror="this.src='https://conteudo.cbf.com.br/cdn/imagens/escudos/empty.jpg'" class="icon escudo m-r-10"> <span class="hidden-xs">Flamengo - RJ</span>
</td>
<th scope="row">90</th>
<td>37</td> <td>28</td>
<td>6</td>
<td>3</td>
<td class="hidden-md hidden-xs">86</td>
<td class="hidden-md hidden-xs">33</td>
<td class="hidden-md hidden-xs">53</td>
<td class="hidden-md hidden-xs">87</td>
<td class="hidden-md hidden-xs">3</td>
<td class="hidden-md hidden-xs">81</td>
<td>
<span class="badge bg-green">V</span>
<span class="badge bg-green">V</span> <span class="badge bg-green">V</span></td> <td><img src="https://conteudo.cbf.com.br/cdn/imagens/escudos/00018sp.jpg" title="Santos - SP" alt="Santos - SP" onerror="this.src='https://conteudo.cbf.com.br/cdn/imagens/escudos/empty.jpg'" class="icon escudo">
</td>
</tr>
Vamos selecionar apenas uma parte da tabela, as colunas que vamos utilizar são posição, pontos, jogos, vitórias, empates e derrotas. Com base na estrutura das linhas da tabela, que você pode ver acima, nós temos o seguinte:
- A posição fica dentro do primeiro TD, no elemento B
- O escudo do clube fica dentro do primeiro TD, no elemento IMG
- O nome do clube fica dentro do primeiro TD, no segundo elemento SPAN
- Os pontos ficam na segunda coluna, que é um elemento TH
- Os jogos ficam na terceira coluna, que é o segundo elemento TD
- As vitorias ficam na quarta coluna, que é o terceiro elemento TD
- Os empates ficam na quinta coluna, que é o quarto elemento TD
- As derrotas ficam na sexta coluna, que é o quinto elemento TD
Vamos começar com um exemplo simples, selecionar a quantidade de jogos. Para fazer isso, o código seria o seguinte:
request(url, function(error, response, html) {
if (!error) {
var resultados = [];
var $ = cheerio.load(html);
$(".tabela-expandir")
.find("tr")
.each(function(i) {
var jogos = $(this)
.find("td")
.eq(1)
.text()
.trim();
console.log(jogos);
});
}
});
Como estamos fazendo um loop por todas as linhas de nossa tabela (TR), o "THIS" se refere a linha atual. Os seletores que foram inseridos tem a seguinte utilidade:
- .find("td") - Seleciona os elementos TD
- .eq(1) - Dentro dos TDs selecionados, retorna o segundo elemento TD (A contagem dos elementos começa com zero)
- .text() - Extrai o conteúdo desta coluna
- .trim() - Remove espaços em branco no começo e no final.
Vamos fazer algo mais complexo, selecionar o escudo do clube. Para isso, o nosso código ficaria assim:
request(url, function(error, response, html) {
if (!error) {
var resultados = [];
var $ = cheerio.load(html);
$(".tabela-expandir")
.find("tr")
.each(function(i) {
var escudo = $(this)
.find("td")
.eq(0)
.find("img")
.attr("src");
console.log(escudo);
});
}
});
Explicando os seletores utilizados:
- .find("td") - Seleciona os elementos TD
- .eq(1) - Dentro dos TDs selecionados, retorna o primeiro elemento TD (A contagem dos elementos começa com zero)
- .find("img") - Seleciona o elemento IMG.
- .attr("src") - Retorna o conteúdo do atributo SRC, que é onde tem o caminho da imagem.
E para retornar o nome do clube? Nosso código ficaria assim:
request(url, function(error, response, html) {
if (!error) {
var resultados = [];
var $ = cheerio.load(html);
$(".tabela-expandir")
.find("tr")
.each(function(i) {
var clube = $(this)
.find("td")
.eq(0)
.find("span")
.eq(1)
.text()
.trim();
console.log(clube);
});
}
});
Os seletores que utilizamos fazem o seguinte:
- .find("td") - Seleciona os elementos TD
- .eq(1) - Dentro dos TDs selecionados, retorna o segundo elemento TD (A contagem dos elementos começa com zero).
- .find("span") - Seleciona todos os elementos SPAN.
- .eq(1) - Retorna o conteúdo do segundo SPAN (A contagem dos elementos começa com zero).
- .text() - Extrai o conteúdo desta coluna
- .trim() - Remove espaços em branco no começo e no final.
Para selecionar os dados necessários, vamos combinar os seletores que acabamos de aprender.
Segue abaixo o código completo. Vamos criar um array para guardar os resultados, e vamos gravar em disco na mesma pasta do projeto, num arquivo com o nome de "resultados.json".
const fs = require("fs");
const request = require("request");
const cheerio = require("cheerio");
const url =
"https://www.cbf.com.br/futebol-brasileiro/competicoes/campeonato-brasileiro-serie-a/2019";
request(url, function(error, response, html) {
if (!error) {
var resultados = [];
var $ = cheerio.load(html);
$(".tabela-expandir")
.find("tr")
.each(function(i) {
var escudo = $(this)
.find("td")
.eq(0)
.find("img")
.attr("src");
var posicao = $(this)
.find("td")
.eq(0)
.find("b")
.text()
.trim();
var clube = $(this)
.find("td")
.eq(0)
.find("span")
.eq(1)
.text()
.trim();
var pontos = $(this)
.find("th")
.eq(0)
.text()
.trim();
var jogos = $(this)
.find("td")
.eq(1)
.text()
.trim();
var vitorias = $(this)
.find("td")
.eq(2)
.text()
.trim();
var empates = $(this)
.find("td")
.eq(3)
.text()
.trim();
var derrotas = $(this)
.find("td")
.eq(4)
.text()
.trim();
// Inserindo os dados num array
if (jogos !== "") {
resultados.push({
posicao,
escudo,
clube,
pontos,
jogos,
vitorias,
empates,
derrotas
});
}
});
fs.writeFile(
"resultados.json",
JSON.stringify(resultados, null, 4),
function(err) {
console.log(
"Resultados salvos com sucesso! O arquivo está na raiz do projeto."
);
}
);
} else {
console.log("Houve um problema ao abrir o endereço informado");
}
});