Este es el tercer artículo de la serie. En «Parser del DOM» aprendimos a convertir la página en un árbol de nodos, y en el artículo sobre selectores CSS — a direccionar con precisión los elementos necesarios de ese árbol. Pero cuando el nodo ya está localizado, dentro suele haber texto «sucio»: «Precio: 12 990 € (antes 15 990)», un teléfono con caracteres de más, una referencia mezclada con la descripción. Para extraer de esa cadena justo el número o el código están las expresiones regulares.
Una expresión regular (regex) — es un patrón que describe un conjunto de cadenas. El motor de regex recorre el texto y encuentra los fragmentos que encajan con el patrón. En el scraping de sitios web es una herramienta imprescindible de limpieza final y extracción de datos.
Cuándo hacen falta las regex — y cuándo no
Primero, una advertencia importante. Con expresiones regulares no se parsea el HTML completo. El HTML es una estructura anidada y no regular; intentar desmontarla con un único patrón conduce a un código frágil e ilegible. Para navegar por el marcado existen los parsers del DOM y los selectores CSS de los artículos anteriores.
Las regex brillan en otro papel — procesar un texto corto ya extraído:
- sacar la cifra del precio de una cadena con moneda y espacios;
- normalizar un teléfono o un email;
- extraer la referencia, el SKU, una fecha, el importe del descuento;
- desmenuzar parámetros de una URL o del texto de un
<script>.
La regla es simple: la estructura se toma con selectores; el valor dentro del nodo — con una regex.
Sintaxis básica
La mayoría de los caracteres de un patrón se representan a sí mismos: el patrón precio encontrará la subcadena «precio». La potencia llega con los caracteres especiales (metacaracteres).
| Símbolo | Significado |
|---|---|
. |
cualquier carácter (salvo el salto de línea) |
\d |
dígito 0–9 |
\D |
no dígito |
\w |
letra, dígito o _ |
\W |
lo contrario de \w |
\s |
carácter de espacio (espacio, tabulador, salto) |
\S |
carácter que no es de espacio |
\b |
límite de palabra |
Para usar un metacarácter como carácter normal (por ejemplo, el punto de un número), se escapa con barra invertida: \., \$, \(.
Clases de caracteres
Los corchetes definen un conjunto de caracteres admisibles en una posición:
[aeiou] una de las vocales enumeradas
[0-9] cualquier dígito (lo mismo que \d)
[a-zA-Z] cualquier letra latina básica
[a-zA-ZáéíóúüñÁÉÍÓÚÜÑ] cualquier letra española, con tildes y eñe
[^0-9] cualquier carácter SALVO un dígito (^ dentro de corchetes = negación)
[\d.,] un dígito, un punto o una comaLos rangos (a-z, 0-9) y las enumeraciones se combinan. ^ al comienzo de la clase invierte el conjunto.
Cuantificadores: cuántas repeticiones
El cuantificador indica cuántas veces se repite el elemento anterior.
| Cuantificador | Cuántas veces |
|---|---|
* |
0 o más |
+ |
1 o más |
? |
0 o 1 (opcional) |
{3} |
exactamente 3 |
{2,5} |
de 2 a 5 |
{2,} |
2 o más |
\d+ uno o más dígitos: «12990»
\d{1,3} de uno a tres dígitos (un grupo de millares)
colou?r «color» y «colour»Voracidad y pereza
Por defecto, los cuantificadores son voraces — capturan todo lo posible. Un ? añadido los vuelve perezosos — capturan lo mínimo:
".*" voraz: de la primera comilla a la ÚLTIMA de la cadena
".*?" perezoso: de la primera comilla a la MÁS PRÓXIMAEs una de las trampas más frecuentes: el patrón voraz «se come» texto de más. En la práctica, para extraer valores cortos casi siempre conviene la variante perezosa o una clase de caracteres más estrecha en lugar de ..
Anclas y límites
Las anclas no coinciden con caracteres — fijan el patrón a una posición:
^inicio ^ — comienzo de la cadena
fin$ $ — final de la cadena
^\d+$ la cadena entera se compone de dígitos
\bSKU\b «SKU» como palabra independiente, no como parte de otraLas anclas protegen de falsos positivos: ^\d+$ garantiza que en la celda hay solo un número, y no «12 uds».
Grupos
Los paréntesis ( ) agrupan una parte del patrón y capturan el fragmento coincidente para recuperarlo después.
(\d+)[.,](\d+) la parte entera y la decimal del número por separadoLos grupos se numeran de izquierda a derecha: el grupo 1 — (\d+) antes del separador; el grupo 2 — el de después.
Grupos sin captura
Si los paréntesis solo se necesitan para agrupar (por ejemplo, bajo un cuantificador) pero el resultado no interesa — se usa (?: ):
(?:https?://)?(\S+) «http://» o «https://» opcional, sin guardarloGrupos con nombre
Para no contar números, al grupo se le da un nombre (?P<name>...) (en Python) o (?<name>...):
(?P<eur>\d+)[.,](?P<cent>\d{2})Después se accede con match.group("eur") — legible y resistente a la reordenación de los grupos.
Referencias hacia atrás
Dentro del patrón se puede hacer referencia a un grupo ya capturado mediante \1, \2:
<(\w+)>.*?</\1> etiqueta emparejada: la de apertura y la de cierre coinciden en el nombre
(["']).*?\1 cadena entre comillas simples o dobles\1 exige que en la segunda posición aparezca el mismo carácter que coincidió en el primer grupo.
Comprobaciones hacia delante y hacia atrás
Las construcciones lookaround comprueban el contexto sin incluirlo en el resultado — muy útiles para «engancharse» a una etiqueta y devolver solo el valor.
| Construcción | Significado |
|---|---|
(?=...) |
delante hay... (positive lookahead) |
(?!...) |
delante NO hay... (negative lookahead) |
(?<=...) |
detrás hay... (positive lookbehind) |
(?<!...) |
detrás NO hay... (negative lookbehind) |
\d+(?=\s*€) número al que SIGUE el símbolo del euro — se devuelve solo el número
(?<=Precio:\s)\d+ número al que PRECEDE «Precio: » — se devuelve solo el número
\d+(?!\d) el último dígito del númeroEl lookbehind resulta especialmente cómodo cuando hay que extraer el valor situado tras una etiqueta fija sin arrastrar la propia etiqueta al resultado.
Flags (modificadores)
Los flags cambian el comportamiento de todo el patrón:
| Flag (Python) | Efecto |
|---|---|
re.I / (?i) |
sin distinguir mayúsculas y minúsculas |
re.S / (?s) |
. coincide también con el salto de línea |
re.M / (?m) |
^ y $ actúan en cada línea |
re.X / (?x) |
modo «verboso» — permite comentar el patrón |
re.findall(r"precio", text, re.I) # «Precio», «PRECIO», «precio»Ejemplos prácticos para el scraping
Reunamos los patrones típicos en Python (módulo re). Funciones útiles: re.search (primera coincidencia), re.findall (todas, como lista), re.sub (sustitución), re.finditer (iterador con grupos).
El precio desde una cadena «sucia»:
import re
text = "Precio: 12 990,50 € (antes 15 990 €)"
# Extraemos el primer número ignorando los espacios que separan los millares
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.5Todos los precios del texto (el nuevo y el antiguo):
prices = re.findall(r"\d[\d\s]*\d(?=\s*€)", text)
# ['12 990', '15 990'] → después los limpiamos de espacios
clean = [int(re.sub(r"\D", "", p)) for p in prices] # [12990, 15990]Referencia / SKU:
sku = re.search(r"\bReferencia[:\s]+([A-Z0-9\-]+)", text)Teléfono (normalización a dígitos):
digits = re.sub(r"\D", "", "+34 912 345 678") # «34912345678»Email:
emails = re.findall(r"[\w.+-]+@[\w-]+\.[\w.-]+", text)Extraer un valor JSON de un script inline:
# En la página de producto, el precio suele estar en un <script> como window.__DATA__ = {...}
m = re.search(r'"price"\s*:\s*"?(\d+(?:\.\d+)?)"?', script_text)Las regex en combinación con el DOM y CSS
Donde más rinde la regex no es en lugar de las herramientas de los artículos anteriores, sino junto a ellas:
from bs4 import BeautifulSoup
import re
soup = BeautifulSoup(html, "html.parser")
# 1) El selector CSS obtiene el nodo necesario
raw = soup.select_one("span.price").get_text(strip=True)
# 2) La regex limpia el texto hasta dejar el número
price = int(re.sub(r"\D", "", raw))El selector responde al «dónde»; la regex — al «qué exactamente de ese texto». Esta separación hace que el scraper sea a la vez preciso y legible.
Aplicación en la monitorización de precios
En la monitorización de precios de tiendas online, las expresiones regulares cubren la «última milla» de la extracción de datos:
- Normalización del precio a un formato numérico único: quitar los espacios separadores y la moneda, y convertir la coma en punto — de lo contrario es imposible comparar los valores de distintas plataformas.
- Desglose del descuento en cadenas como «−23 %» o «ahorro de 3000 €».
- Limpieza de la disponibilidad: de «quedan 4 uds.» extraer la cantidad
\d+. - Identificadores de producto — SKU, EAN/código de barras (
\b\d{13}\b), referencia — para el emparejamiento de un mismo producto entre distintos vendedores. - Datos desde los scripts: muchas tiendas guardan el precio en un JSON dentro de
<script type="application/ld+json">o en variables inline — la regex extrae el campo necesario sin un parseo completo del JS.
La combinación «selector → regex» es la base operativa del motor de scraping de sitios web: recorre las fichas de producto según un calendario, extrae los nodos por CSS y convierte su contenido en números limpios mediante expresiones regulares.
Rendimiento y escollos
- Compile los patrones de uso frecuente —
re.compile(...)una sola vez, en lugar de volver a analizar el patrón en un bucle sobre miles de productos. - Evite el backtracking catastrófico. Los cuantificadores ambiguos anidados como
(\d+)+o(.*)*pueden «colgarse» con una cadena elegida a propósito (o por casualidad). Mantenga estrechas las clases de caracteres y prefiera los cuantificadores perezosos a los voraces cuando proceda. - No complique de más. Si el patrón se ha vuelto ilegible, divida la tarea en dos regex simples o vuelva a los selectores: quizá el valor buscado esté en un atributo
data-aparte que resulta más fácil tomar directamente (vea el artículo sobre selectores CSS). - Pruebe con ejemplos reales. Los precios se escriben de mil maneras:
1 990,1.990,1990,00,desde 1990. Pase el patrón por un conjunto de cadenas reales de distintas tiendas.
Conclusión
Las expresiones regulares son una herramienta para extraer valores del texto, no para desmontar estructuras. Los metacaracteres y las clases describen qué caracteres se esperan; los cuantificadores — cuántos; los grupos y el lookaround — cómo aislar el fragmento buscado en su contexto. En pareja con el parseo del DOM y los selectores CSS forman la cadena completa del scraping de sitios web: encontrar el nodo, extraer el texto, convertirlo en datos limpios. Esa combinación es justamente la que sostiene una monitorización de precios de tiendas online fiable, donde de la maquetación dispar de los competidores hay que obtener con regularidad cifras comparables.
Con esto queda cerrada la serie básica sobre herramientas: el DOM aporta el árbol; los selectores CSS — el direccionamiento; las expresiones regulares — la limpieza final de los datos.