Voltar ao blogue
A ciência da extração de dados da Web
Ștefan Răcilă15 de dezembro de 20227 minutos de leitura

Guia rápido de seletores CSS - Dicas e truques para extrair dados da Web

Guia rápido de seletores CSS - Dicas e truques para extrair dados da Web

Introdução ao DOM

No processo de análise de um ficheiro HTML, o navegador cria uma representação dos dados na sua memória que se assemelha a uma árvore. Esta representação é chamada de DOM (Document Object Model). Para cada tag HTML, existe um nó emparelhado com ela no DOM. Um nó possui propriedades como nome, conteúdo, nós filhos, estilos, eventos, etc. Pode encontrar mais informações sobre como funciona a renderização do navegador neste artigo Como funciona a renderização do navegador — nos bastidores.

Quando dizemos que queremos aceder a dados de uma página web, o que pretendemos é percorrer o 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 seletores CSS.

O que são seletores CSS?

Porque é que são chamados de seletores CSS (Cascading Style Sheets)? 

O CSS é utilizado para definir a aparência dos nós numa página. Com o CSS, pode escrever regras sobre como deve ser a aparência de um nó e como este deve interagir com outros nós. Uma regra é composta por um seletor e uma lista de estilos a substituir.

Portanto, estes seletores estão associados ao CSS porque esta é a sua utilização mais comum, mas não precisamos de os utilizar apenas com CSS. Com o CSS, pretende-se 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 acionar um evento. 

Como funcionam os seletores CSS?

Será de grande ajuda se visualizar a seleção a ocorrer. Digamos que pretende extrair todos os parágrafos de um site. Pretende obter todos os nós que tenham o nome `p`. Pode fazer isso manualmente. Basta percorrer todos os nós no DOM e selecionar apenas os nós que tenham node.tagName === 'P' (os nomes das tags estão em maiúsculas).

Aqui está um pequeno trecho de código que pode usar:

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 este aspeto:

Example webpage card showing headings and paragraphs with IDs and CSS classes like .text and .bold

E aqui está o HTML correspondente:

<!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 navegador, obtive esta resposta:

Code snippet showing HTML <p> elements with id and class attributes under a function call scrapeByTagName(document, 'p')

Como pode ver, a função registou todas as tags p.

Para ver a consola do navegador, tem de abrir o devTools e ir para o separador da consola ou premir a tecla Escape. Pode abrir o devTools clicando com o botão direito do rato num elemento e selecionando «Inspecionar» no menu ou utilizando o atalho de teclado Control + Shift + I.

Como usar seletores CSS?

Vamos usar dois métodos: querySelector e querySelectorAll. Estes métodos estão disponíveis 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.

O querySelector irá devolver o primeiro nó que corresponda ao seletor. O querySelectorAll irá devolver uma lista com todos os nós que correspondam ao seletor. Para replicar o exemplo mostrado anteriormente, basta chamar o querySelectorAll e percorrer a lista devolvida.

document.querySelectorAll('p').forEach(node => console.log(node))
JavaScript console snippet using document.querySelectorAll('p') to log paragraph elements with various classes and IDs

Pode ver que utilizei document.querySelectorAll; isso deve-se ao facto de document estar 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 realmente extrair algo, terá de utilizar uma biblioteca que consiga abrir uma janela do navegador e aceder a um URL. Só então o seu código será executado, no contexto dessa janela. Para saber mais sobre como fazer isto, 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 headless. Pode usar a nossa API para extrair dados de um site sem criar um scraper personalizado. Na verdade, temos um parâmetro chamado extract_rules que usa seletores CSS para extrair dados de uma determinada URL.

Folha de referência dos seletores CSS

O seletor *

Este seletor especifica todos os elementos da árvore. Não tem grande utilidade, mas é bom saber.

O seletor .class

Pode obter um nó com uma classe específica utilizando .class. É utilizado principalmente quando tem uma lista de itens. Como os itens de uma lista tendem a ter o mesmo aspeto, podem ter a mesma classe. Vamos procurar a classe .text.

Code snippet using `document.querySelectorAll('.text')` to iterate over elements, with sample paragraph tags

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

JavaScript console example selecting elements with class .bold and logging two paragraph nodes

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.

JavaScript console example selecting .text.bold and logging one paragraph element with classes text and bold

Tenha em atenção que não há espaços entre as classes.

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 devolveria o elemento filho, caso fosse 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 alcançar 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.

Code snippet using `document.querySelector('#text')` returning a paragraph element with id text

O seletor de nome de nó

Cada nó tem um nome. É o nome exato da tag correspondente no HTML. Pode obter todos os nós que tenham um nome específico utilizando o seu nome no seletor.

JavaScript console output listing two <div> elements with ids wrapper and container

O seletor [atributo] 

Pode deparar-se com situações em que deseja selecionar todos os nós que tenham um determinado atributo.

JavaScript console example selecting elements with a custom-attr attribute, showing heading and paragraph nodes

Também pode especificar o valor do atributo.

JavaScript console example selecting [custom-attr=

Ou até mesmo o que o valor do atributo deve conter. Pode usar o til ~ antes do sinal de igual para definir que o valor do atributo deve conter uma lista de palavras.

JavaScript console example selecting [custom-attr~=

O seletor de atributos será o mais utilizado se decidires criar um scrapper. É muito poderoso e tem muito mais casos de utilização do que aqueles que mostrei aqui. Podes encontrar mais informações sobre como utilizar o seletor de atributos aqui: Seletores de Atributos W3.

Agrupamento de vários seletores

Obter todos os nós p que tenham um id.

JavaScript console example selecting p[id], returning a paragraph element with id text

Selecionar todos os nós span que são filhos de um nó p.

JavaScript console example selecting p span, returning a span element inside a paragraph

Obter todos os nós div que são filhos diretos do nó body.

JavaScript console example selecting body > div, returning a wrapper div element

Obter todos os nós p que tenham a classe .text

JavaScript console example selecting p.text, returning paragraph elements with class text in normal, bold, and italic variants

As opções de agrupamento destes seletores são infinitas. Tente copiar o código HTML acima e adicione mais nós. Depois, experimente diferentes combinações de seletores. Se quiser saber mais sobre seletores CSS em geral, a Mozilla disponibiliza um artigo fantástico que explica como os seletores CSS funcionam no desenvolvimento web.

Resumo

Se quiser aprender algo novo, aconselho-o a aprender primeiro como isso funciona. Sim, é um passo opcional, mas irá dar-lhe algumas informações que os outros não têm. 

No campo do desenvolvimento de software, esta informação irá ajudá-lo a procurar a resposta certa para o seu problema/erro. Poderá tomar o assunto nas suas próprias mãos e até criar uma solução personalizada.

Se queres realmente compreender os seletores CSS, precisas de compreender o DOM. É apenas uma árvore (um grafo acíclico não direcionado) com nós que têm um nome e alguns atributos. É isso mesmo. Quando escreves um seletor, estás apenas a escrever uma cadeia de caracteres que é analisada e utilizada para consultar o DOM.

Sobre o autor
Ștefan Răcilă, Desenvolvedor Full Stack @ WebScrapingAPI
Ștefan RăcilăDesenvolvedor Full Stack

Stefan Racila é engenheiro de DevOps e Full Stack na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante a fiabilidade da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.