Tecnologias e proteções 8 min de leitura

Expressões regulares para o parsing: sintaxe, padrões e limites

Expressões regulares no parsing de dados: sintaxe básica, padrões úteis, limites de aplicação e por que o HTML exige um parser de verdade, e não regex.

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

Este é o terceiro artigo da série. Em «Parser do DOM» aprendemos a transformar a página em uma árvore de nós e, no artigo sobre seletores CSS, a endereçar com precisão os elementos necessários dessa árvore. Mas, quando o nó já foi localizado, dentro dele costuma haver texto «sujo»: «Preço: 12 990 € (antes 15 990)», um telefone com caracteres a mais, um código de referência misturado à descrição. É para extrair dessa string exatamente o número ou o código que existem as expressões regulares.

Uma expressão regular (regex) é um padrão que descreve um conjunto de strings. O motor de regex percorre o texto e encontra os trechos que se encaixam no padrão. No scraping de sites, é uma ferramenta indispensável de limpeza final e extração de dados.

Quando as regex são necessárias — e quando não

Primeiro, um aviso importante. Não se faz o parsing do HTML inteiro com expressões regulares. O HTML é uma estrutura aninhada e não regular; tentar desmontá-la com um único padrão leva a um código frágil e ilegível. Para navegar pela marcação existem os parsers do DOM e os seletores CSS dos artigos anteriores.

As regex brilham em outro papel — processar um texto curto já extraído:

  • tirar o valor numérico do preço de uma string com moeda e espaços;
  • normalizar um telefone ou um email;
  • extrair a referência, o SKU, uma data, o valor do desconto;
  • destrinchar parâmetros de uma URL ou do texto de um <script>.

A regra é simples: a estrutura se pega com seletores; o valor dentro do nó, com uma regex.

Sintaxe básica

A maioria dos caracteres de um padrão representa a si mesma: o padrão preço encontrará a substring «preço». O poder vem com os caracteres especiais (metacaracteres).

Símbolo Significado
. qualquer caractere (exceto a quebra de linha)
\d dígito 0–9
\D não dígito
\w letra, dígito ou _
\W o oposto de \w
\s caractere de espaço (espaço, tabulação, quebra)
\S caractere que não é espaço
\b fronteira de palavra

Para usar um metacaractere como caractere comum (por exemplo, o ponto de um número), escape-o com barra invertida: \., \$, \(.

Classes de caracteres

Os colchetes definem um conjunto de caracteres admissíveis em uma posição:

code
[aeiou]                  uma das vogais listadas
[0-9]                    qualquer dígito (o mesmo que \d)
[a-zA-Z]                 qualquer letra latina básica
[a-zA-ZáàâãéêíóôõúçÁÀÂÃÉÊÍÓÔÕÚÇ]   qualquer letra portuguesa, com acentos e cedilha
[^0-9]                   qualquer caractere EXCETO dígito (^ dentro de colchetes = negação)
[\d.,]                   um dígito, um ponto ou uma vírgula

Os intervalos (a-z, 0-9) e as enumerações podem ser combinados. ^ no início da classe inverte o conjunto.

Quantificadores: quantas repetições

O quantificador indica quantas vezes o elemento anterior se repete.

Quantificador Quantas vezes
* 0 ou mais
+ 1 ou mais
? 0 ou 1 (opcional)
{3} exatamente 3
{2,5} de 2 a 5
{2,} 2 ou mais
code
\d+               um ou mais dígitos: «12990»
\d{1,3}           de um a três dígitos (um grupo de milhar)
colou?r           «color» e «colour»

Quantificadores gulosos e preguiçosos

Por padrão, os quantificadores são gulosos — capturam o máximo possível. Um ? a mais os torna preguiçosos — capturam o mínimo:

code
".*"              guloso: da primeira aspa até a ÚLTIMA da string
".*?"             preguiçoso: da primeira aspa até a MAIS PRÓXIMA

É uma das armadilhas mais frequentes: o padrão guloso «engole» texto demais. Na prática, para extrair valores curtos, quase sempre convém a variante preguiçosa ou uma classe de caracteres mais estreita no lugar de ..

Âncoras e fronteiras

As âncoras não casam com caracteres — elas fixam o padrão a uma posição:

code
^começo            ^ — início da string
fim$               $ — final da string
^\d+$              a string inteira é composta de dígitos
\bSKU\b            «SKU» como palavra independente, não como parte de outra

As âncoras protegem contra falsos positivos: ^\d+$ garante que a célula contém apenas um número, e não «12 un.».

Grupos

Os parênteses ( ) agrupam uma parte do padrão e capturam o trecho correspondente para recuperá-lo depois.

code
(\d+)[.,](\d+)        a parte inteira e a parte decimal do número em separado

Os grupos são numerados da esquerda para a direita: o grupo 1 é (\d+) antes do separador; o grupo 2, o que vem depois.

Grupos sem captura

Se os parênteses servem apenas para agrupar (por exemplo, sob um quantificador), mas o resultado não interessa, usa-se (?: ):

code
(?:https?://)?(\S+)   «http://» ou «https://» opcional, sem armazená-lo

Grupos nomeados

Para não ficar contando números, o grupo recebe um nome (?P<name>...) (em Python) ou (?<name>...):

code
(?P<eur>\d+)[.,](?P<cent>\d{2})

Depois, o acesso é feito com match.group("eur") — legível e resistente à reordenação dos grupos.

Referências retroativas

Dentro do padrão é possível referenciar um grupo já capturado com \1, \2:

code
<(\w+)>.*?</\1>       tag pareada: a de abertura e a de fechamento têm o mesmo nome
(["']).*?\1           string entre aspas simples ou duplas

\1 exige que na segunda posição apareça o mesmo caractere que casou no primeiro grupo.

Verificações à frente e atrás

As construções lookaround verificam o contexto sem incluí-lo no resultado — muito úteis para «se prender» a um rótulo e devolver apenas o valor.

Construção Significado
(?=...) à frente há... (positive lookahead)
(?!...) à frente NÃO há... (negative lookahead)
(?<=...) atrás há... (positive lookbehind)
(?<!...) atrás NÃO há... (negative lookbehind)
code
\d+(?=\s*€)           número SEGUIDO do símbolo do euro — retorna só o número
(?<=Preço:\s)\d+      número PRECEDIDO de «Preço: » — retorna só o número
\d+(?!\d)             o último dígito do número

O lookbehind é especialmente prático quando é preciso extrair o valor que vem depois de um rótulo fixo sem arrastar o próprio rótulo para o resultado.

Flags (modificadores)

As flags mudam o comportamento do padrão inteiro:

Flag (Python) Efeito
re.I / (?i) sem diferenciar maiúsculas e minúsculas
re.S / (?s) . casa também com a quebra de linha
re.M / (?m) ^ e $ atuam em cada linha
re.X / (?x) modo «verboso» — permite comentar o padrão
python
re.findall(r"preço", text, re.I)   # «Preço», «PREÇO», «preço»

Exemplos práticos para o scraping

Vamos reunir os padrões típicos em Python (módulo re). Funções úteis: re.search (primeira ocorrência), re.findall (todas, em lista), re.sub (substituição), re.finditer (iterador com grupos).

O preço a partir de uma string «suja»:

python
import re

text = "Preço: 12 990,50 €  (antes 15 990 €)"

# Extraímos o primeiro número ignorando os espaços que separam os milhares
m = re.search(r"(\d[\d\s]*\d|\d)(?:[.,](\d{2}))?", text)
eur = re.sub(r"\s", "", m.group(1))     # «12990»
cent = m.group(2) or "00"               # «50»
price = float(f"{eur}.{cent}")          # 12990.5

Todos os preços do texto (o novo e o antigo):

python
prices = re.findall(r"\d[\d\s]*\d(?=\s*€)", text)
# ['12 990', '15 990']  → depois limpamos os espaços
clean = [int(re.sub(r"\D", "", p)) for p in prices]   # [12990, 15990]

Referência / SKU:

python
sku = re.search(r"\bReferência[:\s]+([A-Z0-9\-]+)", text)

Telefone (normalização para dígitos):

python
digits = re.sub(r"\D", "", "+55 11 91234-5678")   # «5511912345678»

Email:

python
emails = re.findall(r"[\w.+-]+@[\w-]+\.[\w.-]+", text)

Extrair um valor JSON de um script inline:

python
# Na página de produto, o preço costuma estar em um <script> como window.__DATA__ = {...}
m = re.search(r'"price"\s*:\s*"?(\d+(?:\.\d+)?)"?', script_text)

As regex em conjunto com o DOM e o CSS

Onde a regex mais rende não é no lugar das ferramentas dos artigos anteriores, e sim ao lado delas:

python
from bs4 import BeautifulSoup
import re

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

# 1) O seletor CSS obtém o nó necessário
raw = soup.select_one("span.price").get_text(strip=True)

# 2) A regex limpa o texto até sobrar o número
price = int(re.sub(r"\D", "", raw))

O seletor responde ao «onde»; a regex, ao «o que exatamente desse texto». Essa separação torna o scraper preciso e legível ao mesmo tempo.

Aplicação no monitoramento de preços

No monitoramento de preços de lojas online, as expressões regulares cobrem a «última milha» da extração de dados:

  • Normalização do preço para um formato numérico único: remover os espaços separadores e a moeda e trocar a vírgula por ponto — caso contrário, é impossível comparar os valores de plataformas diferentes.
  • Decomposição do desconto em strings como «−23 %» ou «economia de 3000 €».
  • Limpeza da disponibilidade: de «restam 4 un.» extrair a quantidade \d+.
  • Identificadores de produto — SKU, EAN/código de barras (\b\d{13}\b), referência — para o pareamento do mesmo produto entre vendedores diferentes.
  • Dados a partir dos scripts: muitas lojas guardam o preço em um JSON dentro de <script type="application/ld+json"> ou em variáveis inline — a regex extrai o campo necessário sem um parsing completo do JS.

A combinação «seletor → regex» é a base operacional do motor de scraping de sites: ele percorre as fichas de produto conforme um calendário, extrai os nós via CSS e converte o conteúdo deles em números limpos com expressões regulares.

Desempenho e armadilhas

  • Compile os padrões de uso frequentere.compile(...) uma única vez, em vez de reanalisar o padrão em um laço sobre milhares de produtos.
  • Evite o backtracking catastrófico. Quantificadores ambíguos aninhados como (\d+)+ ou (.*)* podem «travar» com uma string escolhida de propósito (ou por acaso). Mantenha as classes de caracteres estreitas e prefira os quantificadores preguiçosos aos gulosos quando fizer sentido.
  • Não complique além da conta. Se o padrão ficou ilegível, divida a tarefa em duas regex simples ou volte aos seletores: talvez o valor buscado esteja em um atributo data- separado, mais fácil de pegar diretamente (veja o artigo sobre seletores CSS).
  • Teste com exemplos reais. Preços são escritos de mil maneiras: 1 990, 1.990, 1990,00, a partir de 1990. Rode o padrão contra um conjunto de strings reais de lojas diferentes.

Conclusão

As expressões regulares são uma ferramenta para extrair valores do texto, não para desmontar estruturas. Os metacaracteres e as classes descrevem quais caracteres são esperados; os quantificadores, quantos; os grupos e o lookaround, como isolar o trecho buscado no seu contexto. Em dupla com o parsing do DOM e os seletores CSS, elas formam a cadeia completa do scraping de sites: encontrar o nó, extrair o texto, transformá-lo em dados limpos. É exatamente essa combinação que sustenta um monitoramento de preços de lojas online confiável, em que é preciso obter com regularidade números comparáveis a partir dos layouts díspares dos concorrentes.

Com isso, fecha-se a série básica sobre ferramentas: o DOM fornece a árvore; os seletores CSS, o endereçamento; as expressões regulares, a limpeza final dos dados.