Saber extrair dados de tabelas HTML com JavaScript pode ser uma competência essencial ao lidar com dados tabulares apresentados na Web. Os sites apresentam frequentemente informações importantes, tais como detalhes de produtos, preços, níveis de stock e dados financeiros, em tabelas. Ser capaz de extrair estas informações é extremamente útil ao recolher dados para todo o tipo de tarefas de análise. Neste artigo, vamos aprofundar o tema das tabelas HTML e criar um programa simples, mas poderoso, para extrair dados dessas tabelas e exportá-los para um ficheiro CSV ou JSON. Vamos utilizar o Node.js e o cheerio para tornar o processo muito fácil.
Compreender a estrutura de uma tabela HTML
As tabelas HTML são uma ferramenta poderosa para marcar dados tabulares estruturados e apresentá-los de uma forma que seja fácil de ler e compreender pelos utilizadores. As tabelas são compostas por dados organizados em linhas e colunas, e o HTML fornece vários elementos diferentes para definir e estruturar esses dados. Uma tabela deve incluir, no mínimo, os seguintes elementos: <table>, <tr> (linha da tabela) e <td> (dados da tabela). Para maior estrutura e valor semântico, as tabelas também podem incluir o elemento <th> (cabeçalho da tabela), bem como os elementos <thead>, <tbody> e <tfoot>.
Vamos explorar as tags com a ajuda de um pequeno exemplo.
Repare como a segunda tabela utiliza uma sintaxe mais específica
A tag <thead> aplicará uma fonte em negrito às células «Fruit» e «Color» na segunda tabela. Para além disso, pode ver como ambas as sintaxes alcançam a mesma organização dos dados.
Ao extrair tabelas da web, é importante estar ciente de que poderá encontrar tabelas escritas com vários graus de especificidade semântica. Por outras palavras, algumas tabelas podem incluir tags HTML mais detalhadas e descritivas, enquanto outras podem utilizar uma sintaxe mais simples e menos descritiva.
Extrair tabelas HTML com Node.js e cheerio
Bem-vindo à parte divertida! Aprendemos sobre a estrutura e a finalidade das tabelas HTML e agora é hora de colocar esse conhecimento em prática, realizando uma tarefa concreta. O nosso alvo para este tutorial é a tabela dos artistas mais vendidos de todos os tempos, disponível em https://chartmasters.org/best-selling-artists-of-all-time/. Começaremos por configurar o nosso ambiente de trabalho e instalar as bibliotecas necessárias. Em seguida, exploraremos o nosso site de destino e criaremos alguns seletores para extrair os dados encontrados na tabela. Depois disso, escreveremos o código para extrair efetivamente os dados e, finalmente, exportá-los-emos para diferentes formatos, como CSV e JSON.
Configurar o ambiente de trabalho
Muito bem, vamos começar o nosso novo projeto! Antes de começar, certifique-se de que tem o node.js instalado. Pode descarregá-lo em https://nodejs.org/en/.
Agora abre o teu editor de código preferido, abre o diretório do teu projeto e executa (no terminal):
npm init -y
Isto irá inicializar um novo projeto e criar um ficheiro package.json padrão.
{
"name": "html_table_scraper", // the name of your project folder
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}Para simplificar, iremos importar os nossos módulos com “require”. Mas se quiser importar módulos usando a instrução “import”, adicione isto ao seu ficheiro package.json:
"type": "module",
// this will enable you to use the import statement// ex: import * as cheerio from 'cheerio';
A opção “main”: “index.js” especifica o nome do ficheiro que é o ponto de entrada do nosso programa. Dito isto, pode agora criar um ficheiro index.js vazio.
Iremos utilizar a biblioteca cheerio para analisar o HTML do nosso site de destino. Pode instalá-la com:
npm install cheerio
Agora abra o ficheiro index.js e inclua-a como um módulo:
const cheerio = require('cheerio');
O ambiente de trabalho básico está configurado. No próximo capítulo, exploraremos a estrutura da tabela dos artistas mais vendidos de todos os tempos.
Testar o site de destino utilizando o DevTools
Ao inspecionar o separador «Elements» das Ferramentas do Desenvolvedor, podemos extrair informações valiosas sobre a estrutura da tabela:
A tabela está armazenada no formato <thead>, <tbody>.
Todos os elementos da tabela têm atribuídos identificadores (id), classes e funções descritivas.
No contexto de um navegador, tem acesso direto ao HTML de uma página web. Isto significa que pode utilizar funções JavaScript como getElementsByTagName ou querySelector para extrair dados do HTML.
Tendo isto em mente, podemos usar a Consola das Ferramentas de Programador para testar alguns seletores.
Vamos extrair os nomes dos cabeçalhos utilizando o atributo role=”columnheader”
Agora vamos extrair os dados da primeira linha utilizando os atributos role=”cell” e role=”row”:
Como pode ver:
Podemos usar “[role=columheader]” para selecionar todos os elementos de cabeçalho.
Podemos usar “tbody [role=row]” para selecionar todos os elementos de linha.
Para cada linha, podemos usar “[role=cell]” para selecionar as suas células.
Uma coisa a ter em conta é que a célula PIC contém uma imagem e devemos escrever uma regra especial para extrair o seu URL.
Implementação de um scraper de tabelas HTML
Agora é hora de tornar as coisas um pouco mais avançadas usando Node.js e cheerio.
Para obter o HTML de um site num projeto Node.js, terá de fazer um pedido fetch ao site. Isto devolve o HTML como uma string, o que significa que não pode usar funções DOM do JavaScript para extrair dados. É aí que entra o cheerio. O cheerio é uma biblioteca que permite analisar e manipular a string HTML como se estivesse no contexto de um navegador. Isto significa que pode usar seletores CSS familiares para extrair dados do HTML, tal como faria num navegador.
É também importante notar que o HTML devolvido por uma solicitação fetch pode ser diferente do HTML que se vê no navegador. Isto deve-se ao facto de o navegador executar JavaScript, o que pode modificar o HTML que é apresentado. Numa solicitação fetch, está apenas a obter o HTML bruto, sem quaisquer modificações de JavaScript.
Apenas para fins de teste, vamos fazer uma solicitação fetch ao nosso site de destino e gravar o HTML resultante num ficheiro local:
//index.js
const fs = require('fs');
(async () => {
const response = await fetch('https://chartmasters.org/best-selling-artists-of-all-time/');
const raw_html = await response.text();
fs.writeFileSync('raw-best-selling-artists-of-all-time.html', raw_html);
})();
// it will write the raw html to raw-best-selling-artists-of-all-time.html
// try it with other websites: https://randomwordgenerator.com/Pode executá-lo com:
node index.js
E obterá:
A estrutura da tabela permanece a mesma. Isto significa que os seletores que encontrámos no capítulo anterior continuam a ser relevantes.
Ok, agora vamos continuar com o código propriamente dito:
Depois de fazer a solicitação fetch para https://chartmasters.org/best-selling-artists-of-all-time/, terá de carregar o HTML bruto no cheerio:
const cheerio = require('cheerio');
(async () => {
const response = await fetch('https://chartmasters.org/best-selling-artists-of-all-time/');
const raw_html = await response.text();
const $ = cheerio.load(raw_html);
})();Com o cheerio carregado, vamos ver como podemos extrair os cabeçalhos:
const headers = $("[role=columnheader]")
const header_names = []
headers.each((index, el) => {
header_names.push($(el).text())
}) //header_names
[
'#',
'PIC',
'Artist',
'Total CSPC',
'Studio Albums Sales',
'Other LPs Sales',
'Physical Singles Sales',
'Digital Singles Sales',
'Sales Update',
'Streams EAS (Update)'
]E a primeira linha:
const first_row = $("tbody [role=row]")[0]
const first_row_cells = $(first_row).find('[role=cell]')
const first_row_data = []
first_row_cells.each((index, f_r_c) => {
const image = $(f_r_c).find('img').attr('src')
if(image) {
first_row_data.push(image)
}
else {
first_row_data.push($(f_r_c).text())
}
}) //first_row_data
[
'1',
'https://i.scdn.co/image/ab6761610000f178e9348cc01ff5d55971b22433',
'The Beatles',
'421,300,000',
'160,650,000',
'203,392,000',
'116,080,000',
'35,230,000',
'03/01/17',
'17,150,000 (01/03/23)'
]Lembras-te de quando extraímos a tabela HTML com JavaScript na consola das Ferramentas de Desenvolvedor do navegador? Neste ponto, replicámos a mesma funcionalidade que implementámos lá, mas no contexto do projeto Node.js. Podes rever o último capítulo e observar as muitas semelhanças entre as duas implementações.
Continuando, vamos reescrever o código para extrair todas as linhas:
const rows = $("tbody [role=row]")
const rows_data = []
rows.each((index, row) => {
const row_cell_data = []
const cells = $(row).find('[role=cell]')
cells.each((index, cell) => {
const image = $(cell).find('img').attr('src')
if(image) {
row_cell_data.push(image)
}
else {
row_cell_data.push($(cell).text())
}
}) rows_data.push(row_cell_data)
})
//rows_data
[
[
'1',
'https://i.scdn.co/image/ab6761610000f178e9348cc01ff5d55971b22433',
'The Beatles',
'421,300,000',
'160,650,000',
'203,392,000',
'116,080,000',
'35,230,000',
'03/01/17',
'17,150,000 (01/03/23)'
],
[
'2',
'https://i.scdn.co/image/ab6761610000f178a2a0b9e3448c1e702de9dc90',
'Michael Jackson',
'336,084,000',
'182,600,000',
'101,997,000',
'79,350,000',
'79,930,000',
'09/27/17',
'15,692,000 (01/06/23)'
],
...
]Agora que obtivemos os dados, vamos ver como podemos exportá-los.
Exportar os dados
Depois de ter obtido com sucesso os dados que deseja extrair, é importante considerar como pretende armazenar a informação. As opções mais populares são .json e .csv. Escolha o formato que melhor atenda às suas necessidades e requisitos específicos.
Exportar os dados para json
Se quiser exportar os dados para .json, deve primeiro agrupar os dados num objeto JavaScript que se assemelhe ao formato .json.
Temos uma matriz de nomes de cabeçalhos (header_names) e outra matriz (rows_data, uma matriz de matrizes) que contém os dados das linhas. O formato .json armazena informações em pares chave-valor. Precisamos agrupar os nossos dados de forma a seguir essa regra:
[ // this is what we need to obtain
{
'#': '1',
PIC: 'https://i.scdn.co/image/ab6761610000f178e9348cc01ff5d55971b22433',
Artist: 'The Beatles',
'Total CSPC': '421,300,000',
'Studio Albums Sales': '160,650,000',
'Other LPs Sales': '203,392,000',
'Physical Singles Sales': '116,080,000',
'Digital Singles Sales': '35,230,000',
'Sales Update': '03/01/17',
'Streams EAS (Update)': '17,150,000 (01/03/23)'
},
{
'#': '2',
PIC: 'https://i.scdn.co/image/ab6761610000f178a2a0b9e3448c1e702de9dc90',
Artist: 'Michael Jackson',
'Total CSPC': '336,084,000',
'Studio Albums Sales': '182,600,000',
'Other LPs Sales': '101,997,000',
'Physical Singles Sales': '79,350,000',
'Digital Singles Sales': '79,930,000',
'Sales Update': '09/27/17',
'Streams EAS (Update)': '15,692,000 (01/06/23)'
}
...
]Eis como pode conseguir isso:
// go through each row
const table_data = rows_data.map(row => {
// create a new object
const obj = {};
// forEach element in header_names
header_names.forEach((header_name, i) => {
// add a key-value pair to the object where the key is the current header name and the value is the value at the same index in the row
obj[header_name] = row[i];
});
// return the object
return obj;
});Agora pode usar a função JSON.stringify() para converter o objeto JavaScript numa cadeia de caracteres .json e, em seguida, gravá-la num ficheiro.
const fs = require('fs');
const table_data_json_string = JSON.stringify(table_data, null, 2)
fs.writeFile('table_data.json', table_data_json_string, (err) => {
if (err) throw err;
console.log('The file has been saved as table_data.json!');
})Exportar os dados para CSV
O formato .csv significa “valores separados por vírgulas”. Se quiser guardar a sua tabela em .csv, terá de a escrever num formato semelhante a este:
id,name,age // the table headers followed by the rows
1,Alice,20
2,Bob,253,Charlie,30
Os dados da nossa tabela consistem numa matriz de nomes de cabeçalhos (header_names) e noutra matriz (rows_data, uma matriz de matrizes) que contém os dados das linhas. Eis como pode gravar estes dados num ficheiro .csv:
let csv_string = header_names.join(',') + '\n'; // add the headers
// forEach row in rows_data
rows_data.forEach(row => {
// add the row to the CSV string
csv_string += row.join(',') + '\n';
});
// write the string to a file
fs.writeFile('table_data.csv', csv_string, (err) => {
if (err) throw err;
console.log('The file has been saved as table_data.csv!');
});Evite ser bloqueado
Já se deparou com o problema de tentar fazer scraping de um site e perceber que a página da qual está a tentar extrair informações não está a carregar totalmente? Isto pode ser frustrante, especialmente se souber que o site está a usar JavaScript para gerar o seu conteúdo. Não temos a capacidade de executar JavaScript como um navegador normal, o que pode levar a dados incompletos ou até mesmo a ser banido do site por fazer demasiados pedidos num curto período de tempo.
Uma solução para este problema é a WebScrapingApi. Com o nosso serviço, basta fazer pedidos à nossa API e esta tratará de todas as tarefas complexas por si. Executará JavaScript, alternará proxies e até tratará de CAPTCHAs.
Veja como pode fazer uma simples solicitação fetch para um <target_url> e gravar a resposta num ficheiro:
const fs = require('fs');
(async () => {
const result = await fetch('https://api.webscrapingapi.com/v1?' + new URLSearchParams({
api_key: '<api_key>',
url: '<target_url>',
render_js: 1,
proxy_type: 'residential',
}))
const html = await result.text();
fs.writeFileSync('wsa_test.html', html);
})();Pode obter uma API_KEY gratuita criando uma nova conta em https://www.webscrapingapi.com/
Ao especificar o parâmetro render_js=1, ativará a capacidade da WebScrapingAPI de aceder à página web de destino utilizando um navegador headless, o que permite que os elementos JavaScript da página sejam renderizados antes de lhe entregar o resultado final da extração.
Visite https://docs.webscrapingapi.com/webscrapingapi/advanced-api-features/proxies para descobrir as capacidades dos nossos proxies rotativos.
Conclusão
Neste artigo, aprendemos sobre o poder do web scraping de tabelas HTML com JavaScript e como isso pode ajudar-nos a extrair dados valiosos de sites. Explorámos a estrutura das tabelas HTML e aprendemos a usar a biblioteca cheerio em combinação com o Node.js para extrair facilmente dados delas. Também analisámos diferentes formas de exportar os dados, incluindo os formatos CSV e JSON. Seguindo os passos descritos neste artigo, deverá agora ter uma base sólida para extrair tabelas HTML em qualquer site.
Quer seja um profissional experiente ou esteja apenas a dar os primeiros passos no seu primeiro projeto de scraping, a WebScrapingAPI está aqui para o ajudar em cada etapa do processo. A nossa equipa terá todo o prazer em responder a quaisquer perguntas que possa ter e em oferecer orientação sobre os seus projetos. Por isso, se alguma vez se sentir bloqueado ou precisar apenas de uma ajuda, não hesite em contactar-nos.




