Introdução ao DOM
No processo de análise de um ficheiro HTML, o browser cria uma representação de dados na sua memória que se assemelha a uma árvore. Esta representação é designada por DOM (Document Object Model). Para cada etiqueta HTML, existe um nó associado a ela no DOM. Um nó tem propriedades como o nome, o conteúdo, os nós filhos, os estilos, os eventos, etc. Pode encontrar mais informações sobre o funcionamento da renderização do browser neste artigo Como funciona a renderização do browser - nos bastidores.
Quando dizemos que queremos aceder a dados de uma página Web, queremos apenas iterar através do DOM até um conjunto específico de nós e extrair o conteúdo dentro deles. Neste artigo, vou dar-lhe várias dicas sobre como aceder rapidamente a esses nós utilizando Selectores CSS.
O que são selectores CSS?
Porque é que se chamam Selectores CSS (Cascading Style Sheets)?
As CSS são utilizadas para definir o aspeto dos nós numa página. Com as CSS, é possível escrever regras sobre o aspeto de um nó e a forma como este deve interagir com outros nós. Uma regra é composta por um seletor e uma lista de estilos a substituir.
Assim, estes selectores estão associados às CSS porque esta é a sua utilização mais comum, mas não precisamos de os utilizar apenas com CSS. Com CSS, pretende selecionar um nó e alterar a sua propriedade de estilo. Se pensarmos bem, queremos fazer a mesma coisa: selecionar um nó e fazer algo com ele, como ler o seu conteúdo ou ativar um evento.
Como funcionam os selectores CSS?
Será muito útil se visualizar a seleção a acontecer. Digamos que você queira extrair todos os parágrafos de um site. Você quer pegar todos os nós que têm o nome `p`. É possível fazer isso manualmente. Você só precisa iterar através de cada nó no DOM e selecionar apenas os nós que têm node.tagName === 'P' (nomes de tags são maiúsculos).
Aqui está um pequeno trecho de código que pode utilizar:
function scrapeByTagName(node, tagName) {
if (node === null)
return;
node.childNodes.forEach(node => {
//console.log(node.tagName)
if (node.tagName?.toLowerCase() === tagName.toLowerCase()) {
console.log(node)
return
}
scrapeByTagName(node, tagName)
});
}
Criei uma página Web fictícia com o seguinte aspeto:

E aqui está o HTML para ele:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<script src="script.js"></script>
</head>
<body>
<div id="wrapper">
<h1 custom-attr="some data">Some Title</h1>
<h2 custom-attr="some other data">Some Subtitle</h2>
<div id="container">
<p custom-attr>paragraph
<span> subparagraph</span>
</p>
<p id="text">paragraph with id #text</p>
<p class="bold">paragraph with class .bold</p>
<p class="text">paragraph with class .text</p>
<p class="text bold">paragraph with class .text.bold</p>
<p class="text italic">paragraph with class .text.italic</p>
</div>
</div>
</body>
</html>
Depois de executar a função na consola do browser, obtive esta resposta:

Como pode ver, a função registou todas as etiquetas p.
Para aceder à consola do navegador, tem de abrir as Ferramentas de Programador e ir para o separador «Consola» ou premir esescapar. Pode abrir o devTools clicando com o botão direito do rato num elemento e selecionando Inspecionarpect no menu ou usando o atalho de teclado control + shift + i.
Como utilizar selectores CSS?
Vamos utilizar dois métodos: querySelector e querySelectorAll. Estes métodos estão presentes em todos os objetos do tipo Element. Os nós que estamos a tentar extrair são do tipo HTMLElement, que herda do tipo Element.
querySeleretornará o primeiro nó que corresponda ao seletor. querySelectorAll irá devolver uma lista com todos os nós que correspondem ao seletor. Para replicar o exemplo mostrado anteriormente, basta chamar querySelectorAll e percorrer a lista devolvida.
document.querySelectorAll('p').forEach(node => console.log(node))

Pode ver que utilizei document.querySelectorAll, isso é porque document está definido no contexto da janela como a raiz da página web, ou seja, o equivalente à tag html. Pode utilizar os métodos querySelector com todos os nós, não apenas com o nó raiz.
Para fazer scrape de algo, terá de utilizar uma biblioteca que abra uma janela do browser e vá para um URL. Só então seu código será executado, no contexto dessa janela. Para saber mais sobre como fazer isso, recomendo este artigo The Ultimate Guide to Web Scraping with JavaScript and Node.Js.
Aqui na WebScrapingAPI usamos o Puppeteer. O Puppeteer é uma biblioteca que nos permite controlar instâncias de navegadores Chromium sem cabeça. Pode utilizar a nossa API para extrair dados de um website sem construir um scraper personalizado. Na verdade, temos um parâmetro chamado extract_rules que usa seletores CSS para extrair dados de um determinado URL.
A folha de dicas sobre seletores CSS
O seletor *
Este seletor especifica todos os elementos da árvore. Não tem muita utilização mas é bom sabê-lo.
O seletor .class
Pode obter um nó com uma classe específica utilizando .class. É utilizado principalmente quando se tem uma lista de itens. Como é provável que os itens de uma lista tenham a mesma aparência, eles podem ter a mesma classe. Vamos procurar a classe .text.

Talvez queira selecionar o nó que tem a classe .bold.

Parece que há outro elemento que tem a classe .bold. Pode ser mais específico com o seletor de classe, utilizando várias classes concatenadas.

Atenção: não há espaços entre as aulas.
document.querySelectorAll('.text .bold').forEach(node => console.log(node))
Esta consulta não devolve nada do HTML acima, porque procura um elemento com a classe .text que tenha um elemento filho com a classe .bold (não necessariamente um elemento filho direto). A consulta retornaria o elemento filho, se encontrado.
O seletor #id
E se um elemento não tiver uma classe ou se a classe for utilizada com demasiada frequência no documento? Pode utilizar o atributo ID para obter um nível mais profundo de especificidade. A desvantagem de utilizar o seletor id é que, na maioria dos casos, o id é único na página HTML, pelo que não é possível obter uma lista de nós com ele.

O Seletor de Nome de Nó
Cada nó tem um nome. É o nome exato da etiqueta emparelhada no HTML. Pode obter todos os nós que têm um nome específico utilizando o seu nome no seletor.

O seletor [atributo
Poderá encontrar situações em que pretenda selecionar todos os nós que tenham um atributo específico.

Também é possível especificar o valor do atributo.
![Exemplo na consola JavaScript que seleciona [custom-attr="some data"] e devolve um único elemento h1](/_next/image?url=https%3A%2F%2Fimages.prismic.io%2Fwebscrapingapi%2Fe6d9c82d-40b5-435b-a583-e355c31c3a54_image8.png%3Fauto%3Dformat%2Ccompress&w=3840&q=75)
Ou mesmo o que o valor do atributo deve conter. Pode utilizar o til ~ antes do sinal de igual para definir que o valor do atributo deve conter uma lista de palavras.
![Exemplo na consola JavaScript que seleciona [custom-attr~="data"], correspondendo aos elementos h1 e h2 com atributos personalizados](/_next/image?url=https%3A%2F%2Fimages.prismic.io%2Fwebscrapingapi%2Fd3160920-a556-43a7-a069-461561f0b188_image9.png%3Fauto%3Dformat%252Ccompress%26rect%3D0%252C0%252C1100%252C119%26w%3D1100%26h%3D119&w=3840&q=75)
O seletor de atributos será o mais utilizado se decidir construir um scrapper. É muito poderoso e tem muito mais casos de utilização do que os que mostrei aqui. Pode encontrar mais informações sobre como utilizar o seletor de atributos aqui W3 Attribute Selectors.
Agrupamento de vários selectores
Obter todos os nós p que têm um id.
![Exemplo na consola JavaScript para selecionar p[id], devolvendo um elemento de parágrafo com o texto do id](/_next/image?url=https%3A%2F%2Fimages.prismic.io%2Fwebscrapingapi%2F71f8d44f-66a5-49c0-8b9d-aff2d8e50b64_image2.png%3Fauto%3Dformat%2Ccompress&w=3840&q=75)
Selecionar todos os nós de extensão que são filhos de um nó p.

Obtém todos os nós div que são filhos diretos do nó body.

Obtém todos os nós p que têm a classe .text

As opções de agrupamento destes selectores são infinitas. Tente copiar o código HTML acima e adicionar mais nós a ele. Em seguida, experimente diferentes combinações de selectores. Se quiser saber mais sobre seletores CSS em geral, a Mozilla oferece um artigo fantástico que explica como os seletores CSS funcionam para o desenvolvimento Web.
Resumo
Se quiser aprender alguma coisa nova, aconselho-o a aprender primeiro como é que essa coisa funciona. Sim, é um passo opcional, mas dar-lhe-á algumas informações que outros não têm.
No domínio do desenvolvimento de software, estas informações ajudá-lo-ão a encontrar a resposta certa para o seu problema/erro. Pode tomar o assunto nas suas próprias mãos e até criar uma solução personalizada.
Se quer realmente compreender os selectores CSS, tem de compreender o DOM. É apenas uma árvore (um gráfico acíclico não direcionado) com nós que têm um nome e alguns atributos. É só isso. Quando escreve um seletor, limita-se a escrever uma cadeia de caracteres que é analisada e utilizada para consultar o DOM.




