Tecnologias e proteções 8 min de leitura

Seletores CSS para web scraping: panorama das construções

Guia rápido de seletores CSS para extrair dados: classes, atributos, pseudoclasses, combinadores e erros típicos ao selecionar elementos de uma página.

EW
Equipe Web-Scraping.biz
Coleta de dados para as demandas do negócio
Publicado: 2 fevereiro 2025

No material anterior — «Parser do DOM» — vimos como o navegador e as bibliotecas transformam o HTML em uma árvore de nós e como percorrer essa árvore via código. Mas percorrer o DOM «na mão», pulando de pai para filho, é tedioso e frágil. Na prática do scraping de sites, o nó buscado quase sempre é localizado com uma única expressão curta: um seletor CSS.

Os seletores CSS nasceram para a estilização de páginas: com eles se descreve a quais elementos aplicar um estilo. Mas essa mesma sintaxe serve maravilhosamente bem para encontrar elementos. Por isso, quase todas as ferramentas de scraping — do BeautifulSoup ao Scrapy ou ao Playwright — aceitam a seleção via CSS. Vamos repassar as construções uma a uma.

Para que servem os seletores CSS no scraping

Um seletor é uma forma declarativa de dizer «me dê todos os elementos com esta cara». Em vez de uma dezena de linhas percorrendo a árvore, escreve-se uma única linha, e o motor desce pelo DOM e retorna as ocorrências. O resultado é:

  • compacto — um seletor no lugar de um laço;
  • legível — a expressão lembra a descrição do lugar que o elemento ocupa na página;
  • portável — o mesmo seletor funciona em Python, em JS e no console do navegador (document.querySelectorAll).

A habilidade-chave de quem scrapeia é olhar a estrutura da página no DevTools e montar um seletor que acerte exatamente o alvo e, de quebra, sobreviva às pequenas mudanças de layout.

Seletores básicos

São os átomos com os quais tudo o mais é construído.

Seletor O que seleciona Exemplo
* qualquer elemento *
div elementos pelo nome da tag a, span, tr
.price elementos com a classe price .product-card
#main o elemento com id="main" #catalog

A classe e a tag podem ser coladas sem espaço — é um «E» lógico: a.button significa «link que tem a classe button». E div.card.featured é um div com duas classes ao mesmo tempo.

css
span.price.current     /* <span class="price current"> */
li#first-item          /* <li id="first-item"> */

Agrupamento

A vírgula une vários seletores em uma única lista: retornam os elementos que casarem com qualquer um deles:

css
h1, h2, h3             /* todos os títulos desses três níveis */
.price-new, .price-old /* tanto o preço novo quanto o antigo */

É útil quando o produto leva a classe price-new em algumas fichas e price-old em outras, e é preciso capturar as duas variantes com uma única consulta.

Combinadores: relações entre elementos

Os combinadores descrevem como os elementos se posicionam uns em relação aos outros dentro da árvore. É a parte mais importante para um scraping robusto.

Descendente (espaço)

A B — um elemento B situado em algum ponto dentro de A, em qualquer profundidade.

css
.product-card .price   /* .price em qualquer ponto dentro da ficha */

Filho direto (>)

A > BB é filho imediato de A. Não desce além disso.

css
ul.menu > li           /* só os itens do primeiro nível do menu, sem os aninhados */

A diferença é de fundo: .menu li capturará também os itens dos submenus; .menu > li, não.

Adjacente (+)

A + B — o elemento B que vem logo depois de A e divide com ele o mesmo pai.

css
h2 + p                 /* o primeiro parágrafo imediatamente depois do título */
.label + .value        /* o valor colado logo atrás do seu rótulo */

Irmãos gerais (~)

A ~ B — todos os B que vêm depois de A sob o mesmo pai (não necessariamente colados a ele).

css
.in-stock ~ .price     /* todos os preços que seguem a marcação «em estoque» */

Seletores de atributos

Com frequência o elemento não tem uma classe conveniente, mas tem um atributo característico. Os seletores de atributos salvam a situação quando as classes são dinâmicas ou cheias de ruído.

Construção Significado
[data-id] o atributo data-id existe (com qualquer valor)
[type="submit"] correspondência exata do valor
[class~="price"] o valor contém a palavra price entre palavras separadas por espaço
[href^="https"] o valor começa com https
[href$=".pdf"] o valor termina em .pdf
[href*="product"] o valor contém a substring product
[lang|="en"] o valor é en ou começa com en-
[type="text" i] a flag i — comparação sem diferenciar maiúsculas de minúsculas
css
a[href^="/catalog/"]           /* links dentro do catálogo */
img[src$=".webp"]              /* imagens no formato webp */
div[data-testid*="product"]    /* contêineres com a marcação de produto em um atributo data */
input[name="csrf"]             /* campo oculto do token */

Os seletores de atributos são especialmente valiosos no monitoramento de preços de lojas online: as grandes plataformas costumam expor os dados em atributos data-* (data-price, data-sku, data-currency), e se prender a eles é mais confiável do que a classes que mudam a cada release do front-end.

Pseudoclasses: seleção por posição e estado

As pseudoclasses filtram os elementos pela posição entre os vizinhos ou por condições adicionais.

Posicionais

css
li:first-child         /* primeiro filho */
li:last-child          /* último filho */
tr:nth-child(2)        /* segunda linha */
tr:nth-child(odd)      /* linhas ímpares */
tr:nth-child(even)     /* linhas pares */
tr:nth-child(3n)       /* uma a cada três */
li:nth-last-child(1)   /* o primeiro contando do fim */

A fórmula an+b de :nth-child() é flexível: nth-child(2n+1) seleciona os ímpares; nth-child(n+3), todos a partir do terceiro.

Por tipo de tag

:nth-child conta todos os irmãos em sequência, enquanto :nth-of-type conta apenas os elementos da mesma tag:

css
p:first-of-type        /* o primeiro parágrafo entre os irmãos */
img:nth-of-type(2)     /* a segunda imagem */
span:last-of-type      /* o último span */

Isso salva a situação quando o <p> buscado é o segundo parágrafo, mas antes dele se infiltraram tags de outro tipo.

Negação e estado

css
input:not([type="hidden"])   /* todos os campos, exceto os ocultos */
li:not(.sold-out)            /* produtos que não constam como esgotados */
a:not([rel="nofollow"])      /* links sem nofollow */

:not() aceita outro seletor dentro e o inverte. Também existem :empty (elementos vazios), :checked, :disabled e outros, mas no scraping estático o que mais se usa são justamente as pseudoclasses posicionais e o :not().

Repare que os pseudoelementos (::before, ::after, ::first-line) não se usam no scraping: eles descrevem conteúdo gerado pelos estilos, que não existe no HTML original.

Montando um seletor complexo

As expressões reais combinam tudo o que vimos. Um exemplo: extrair o preço vigente da ficha de produto de um catálogo:

css
div.product-card a[href^="/product/"] span.price:not(.price--old)

Lê-se assim: dentro da ficha de produto, no link para a página do produto, pegar o span com a classe price que não seja o preço antigo (riscado).

Ferramentas: onde isso funciona

Os seletores CSS têm suporte em praticamente todo o stack de scraping.

Python — BeautifulSoup (por meio da biblioteca soupsieve):

python
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "html.parser")

# .select() retorna uma lista; .select_one(), o primeiro elemento
cards = soup.select("div.product-card")
for card in cards:
    title = card.select_one("h3.title").get_text(strip=True)
    price = card.select_one("span.price:not(.price--old)").get_text(strip=True)
    print(title, price)

Python — parsel / Scrapy (um invólucro sobre o lxml, rápido):

python
from parsel import Selector

sel = Selector(text=html)
prices = sel.css("span.price::text").getall()   # ::text é uma extensão própria do parsel
links = sel.css("a.product-link::attr(href)").getall()

Aqui, ::text e ::attr(name) são pseudoelementos não padronizados do Scrapy/parsel: extraem diretamente o texto ou o atributo, sem precisar de uma chamada à parte.

JavaScript / navegador — API nativa, a mesma sintaxe:

javascript
const cards = document.querySelectorAll("div.product-card");
const price = document.querySelector("span.price:not(.price--old)")?.textContent.trim();

Playwright / Selenium — para páginas dinâmicas, em que os dados são carregados por scripts:

python
# Playwright
price = page.locator("div.product-card span.price").first.inner_text()

Ou seja: uma vez dominados os seletores, você os usará tanto em scrapers HTTP leves quanto em navegadores headless.

Aplicação no monitoramento de preços

Em uma tarefa típica de monitoramento de preços de lojas online, o roteiro é este:

  1. Lista de produtos do catálogo. Um seletor do tipo div.catalog__item localiza todas as fichas na página de resultados.
  2. Link para o produto. De cada ficha, a[href^="/product/"]::attr(href) extrai a URL.
  3. Preço e disponibilidade. Na página do produto, [data-price]::attr(data-price) ou span.price--current fornece o preço, e .availability:not(.out-of-stock) confirma que o produto está à venda.
  4. Preço antigo e desconto. span.price--old captura o preço riscado, para que dê para calcular o tamanho do desconto.

Como cada concorrente monta o layout do seu jeito, para cada plataforma escreve-se um conjunto próprio de seletores, e o motor de scraping de sites os aplica conforme um calendário e despeja o resultado em uma tabela comum para comparação.

Erros frequentes e dicas

  • Não se apegue a classes «lixo». Classes do tipo .css-1ab2c3 são geradas pelos empacotadores e mudam a cada deploy. Prefira atributos data-*, id com significado ou combinadores estruturais.
  • Use > onde a estrutura importa. Assim se descartam coincidências acidentais em blocos aninhados.
  • Verifique o seletor no DevTools. O comando $$("seu seletor") no console do navegador mostra na hora quantos elementos foram encontrados.
  • Preveja variantes. Liste, separados por vírgula, vários possíveis nomes de classe do preço: isso aumenta a resistência aos testes A/B da loja.
  • Lembre-se dos limites do CSS. Os seletores não sabem «subir até o pai» a partir do conteúdo nem filtrar pelo texto do nó. Onde o CSS não alcança, entram em cena o XPath ou as expressões regulares, das quais falaremos no próximo material.

Conclusão

Os seletores CSS são a principal ferramenta de endereçamento dentro do DOM. Os seletores básicos (tag, classe, id) definem o «o quê»; os combinadores (espaço, >, +, ~), o «onde»; e os seletores de atributos, junto com as pseudoclasses, acrescentam condições precisas. Juntos, cobrem a imensa maioria das tarefas de scraping de sites, e um seletor bem construído faz o scraper resistir aos pequenos retoques de layout, algo crítico quando se trata de um monitoramento de preços de lojas online recorrente.

No próximo artigo veremos como extrair dados de texto «bruto» com expressões regulares, lá onde a estrutura do DOM já não basta.