Cómo crear un bot de alerta de precios con Python

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Bot em Python para monitorar produtos e avisar quando houver queda de preço automaticamente

Un bot de alerta de precios consulta periódicamente una página o una API, extrae el valor de un producto y envía una notificación cuando se cumple una condición. Es un proyecto práctico para aprender solicitudes HTTP, análisis de HTML, programación de tareas, almacenamiento de estado y manejo de errores. La parte importante no es únicamente encontrar un número: el sistema debe identificar el producto correcto, evitar alertas duplicadas y respetar las reglas del sitio.

Para seguir esta guía conviene conocer Requests en Python, el web scraping con Beautiful Soup, las variables de entorno, el módulo logging y el manejo de excepciones.

Elegir una fuente adecuada

Siempre que sea posible, utiliza una API oficial, un feed de datos o una función de alertas proporcionada por la tienda. El scraping de HTML es más frágil porque la estructura visual puede cambiar. Antes de automatizar consultas, revisa los términos de uso, el archivo robots.txt y los límites de acceso.

La documentación oficial de Requests explica sesiones, timeouts y errores HTTP. La documentación oficial de Beautiful Soup describe selectores y análisis de documentos HTML.

Instalar las dependencias

python -m venv .venv
.venv\Scripts\activate
python -m pip install requests beautifulsoup4

En macOS o Linux utiliza source .venv/bin/activate. Guarda las versiones utilizadas en un archivo de requisitos para reproducir el entorno.

Descargar la página con seguridad

import requests

URL = "https://ejemplo.com/producto"


def descargar_pagina(url: str) -> str:
    cabeceras = {
        "User-Agent": "Mozilla/5.0 PriceMonitor/1.0"
    }
    respuesta = requests.get(
        url,
        headers=cabeceras,
        timeout=(5, 15),
    )
    respuesta.raise_for_status()
    return respuesta.text

Un timeout de conexión y lectura evita bloqueos indefinidos. No intentes eludir protecciones, captchas o controles de acceso. Si el sitio bloquea automatizaciones, utiliza una alternativa autorizada.

Encontrar el precio

Inspecciona el HTML y busca un selector estable, preferiblemente un atributo semántico:

from bs4 import BeautifulSoup


def extraer_texto_precio(html: str) -> str:
    soup = BeautifulSoup(html, "html.parser")
    elemento = soup.select_one('[data-testid="product-price"]')
    if elemento is None:
        raise ValueError("No se encontró el precio")
    return elemento.get_text(" ", strip=True)

No uses un selector basado solo en una cadena de clases generadas automáticamente, porque puede cambiar en cada despliegue.

Convertir el texto a Decimal

from decimal import Decimal, InvalidOperation
import re


def convertir_precio(texto: str) -> Decimal:
    limpio = re.sub(r"[^0-9,.-]", "", texto)

    if "," in limpio and "." in limpio:
        limpio = limpio.replace(".", "").replace(",", ".")
    elif "," in limpio:
        limpio = limpio.replace(",", ".")

    try:
        precio = Decimal(limpio)
    except InvalidOperation as error:
        raise ValueError(f"Precio inválido: {texto}") from error

    if precio < 0:
        raise ValueError("El precio no puede ser negativo")
    return precio

Las reglas de separadores dependen de la región. Si el sitio utiliza un formato diferente, adapta la función y añade pruebas específicas.

Comprobar el producto correcto

Además del precio, verifica el nombre o un identificador:

def extraer_producto(html: str) -> dict:
    soup = BeautifulSoup(html, "html.parser")
    titulo = soup.select_one("h1")
    precio = soup.select_one('[data-testid="product-price"]')

    if not titulo or not precio:
        raise ValueError("La estructura de la página cambió")

    return {
        "titulo": titulo.get_text(" ", strip=True),
        "precio": convertir_precio(
            precio.get_text(" ", strip=True)
        ),
    }

Esto evita enviar una alerta si la URL fue redirigida a una categoría, página de error o producto alternativo.

Definir el umbral

from decimal import Decimal

PRECIO_OBJETIVO = Decimal("299.90")


def debe_alertar(precio_actual: Decimal) -> bool:
    return precio_actual <= PRECIO_OBJETIVO

También puedes detectar una reducción porcentual respecto al último precio:

def reduccion_porcentual(anterior, actual):
    if anterior <= 0:
        return Decimal("0")
    return (anterior - actual) / anterior * 100

Guardar el estado

Para no repetir la misma alerta en cada ejecución, guarda el último precio y la última notificación:

import json
from pathlib import Path

RUTA_ESTADO = Path("estado_precio.json")


def cargar_estado():
    if not RUTA_ESTADO.exists():
        return {}
    return json.loads(RUTA_ESTADO.read_text(encoding="utf-8"))


def guardar_estado(datos):
    temporal = RUTA_ESTADO.with_suffix(".tmp")
    temporal.write_text(
        json.dumps(datos, indent=2, ensure_ascii=False),
        encoding="utf-8",
    )
    temporal.replace(RUTA_ESTADO)

La escritura temporal reduce el riesgo de dejar un JSON incompleto si el proceso se interrumpe.

Enviar una notificación por correo

import os
import smtplib
from email.message import EmailMessage


def enviar_correo(titulo, precio, url):
    usuario = os.getenv("SMTP_USER")
    clave = os.getenv("SMTP_PASSWORD")
    destino = os.getenv("ALERT_EMAIL")

    if not all([usuario, clave, destino]):
        raise RuntimeError("Faltan variables SMTP")

    mensaje = EmailMessage()
    mensaje["Subject"] = f"Bajó el precio: {titulo}"
    mensaje["From"] = usuario
    mensaje["To"] = destino
    mensaje.set_content(
        f"Precio actual: {precio}\nProducto: {url}"
    )

    with smtplib.SMTP_SSL("smtp.example.com", 465) as servidor:
        servidor.login(usuario, clave)
        servidor.send_message(mensaje)

Utiliza una contraseña de aplicación o un proveedor transaccional. Nunca guardes credenciales directamente en el archivo.

Integrar el flujo

from datetime import datetime, timezone
import logging

logging.basicConfig(
    filename="monitor_precios.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
    encoding="utf-8",
)


def ejecutar():
    html = descargar_pagina(URL)
    producto = extraer_producto(html)
    estado = cargar_estado()

    precio = producto["precio"]
    ultima_alerta = estado.get("precio_alertado")

    if debe_alertar(precio) and str(precio) != ultima_alerta:
        enviar_correo(producto["titulo"], precio, URL)
        estado["precio_alertado"] = str(precio)

    estado["ultimo_precio"] = str(precio)
    estado["ultima_revision"] = datetime.now(
        timezone.utc
    ).isoformat()
    guardar_estado(estado)
    logging.info("Precio comprobado: %s", precio)

Manejar fallos

def main():
    try:
        ejecutar()
    except requests.Timeout:
        logging.warning("Timeout consultando el producto")
    except requests.HTTPError as error:
        logging.error("HTTP %s", error.response.status_code)
    except (ValueError, KeyError, json.JSONDecodeError):
        logging.exception("No se pudo interpretar la información")
    except OSError:
        logging.exception("Error leyendo o guardando el estado")

No envíes una alerta de “precio cero” cuando el selector falla. Un dato ausente debe tratarse como error, no como oferta.

Programar la ejecución

En Windows utiliza el Programador de tareas. En Linux o macOS puedes usar cron o un servicio. Una frecuencia razonable suele ser cada varias horas, dependiendo del permiso y de los límites del sitio. No ejecutes solicitudes cada pocos segundos.

Probar el monitor

Guarda ejemplos de HTML en archivos locales y prueba la extracción sin Internet. Incluye casos con precio normal, descuento, producto agotado, selector ausente y separadores distintos. Simula también timeout, respuesta 500 y correo fallido. Los tests no deben consultar una tienda real.

Mejoras útiles

Puedes monitorizar varios productos mediante un archivo JSON o CSV, añadir historial para crear gráficos, enviar avisos por Telegram o Discord, usar SQLite y aplicar una ventana de silencio para no repetir notificaciones. Si el sitio ofrece datos estructurados JSON-LD con información de producto, puede ser una fuente más estable que el texto visual.

Seguridad y responsabilidad

Limita la frecuencia, identifica el cliente de forma honesta, almacena secretos fuera del repositorio y registra únicamente información necesaria. No recolectes datos personales ni intentes superar medidas técnicas. Una automatización responsable debe poder desactivarse rápidamente y respetar las condiciones de la fuente.

Conclusión

Un bot de alerta de precios combina scraping, validación, persistencia y notificaciones. La solución confiable comprueba el producto, convierte el valor con precisión, utiliza timeouts, evita alertas duplicadas y maneja cambios de estructura. Empieza con una sola página y una frecuencia conservadora. Después añade pruebas, almacenamiento e interfaces sin comprometer el respeto por el sitio consultado.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Extração de texto de arquivos PDF usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo extraer texto de archivos PDF con Python

    Extrae texto de PDF con Python usando pypdf y pdfplumber, procesa carpetas, tablas, archivos protegidos y detecta cuándo necesitas OCR.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Automação de tarefas com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    20 scripts de Python para automatizar tareas cotidianas

    Descubre 20 scripts de Python para organizar archivos, crear backups, procesar CSV, enviar correos, comprobar sitios y automatizar tareas seguras.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Comparando listas e diferenças entre arrays em Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo comparar dos listas en Python y encontrar diferencias

    Compara listas en Python con igualdad, sorted, sets y Counter, conserva duplicados, detecta registros modificados y evita errores frecuentes.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Automação em Python para organizar arquivos do computador
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo organizar automáticamente los archivos de tu PC con Python

    Organiza automáticamente archivos del PC con Python, pathlib y shutil, evitando sobrescrituras, descargas incompletas y movimientos sin registro.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Script Python configurado para iniciar junto com o Windows
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo ejecutar un script de Python al iniciar Windows

    Configura un script Python para iniciar con Windows usando la carpeta Inicio, archivos BAT, Task Scheduler, entornos virtuales, logs y

    Ler mais

    Tempo de leitura: 6 minutos
    11/07/2026
    Sistema de backup automático de arquivos usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo automatizar copias de seguridad con Python

    Aprende a automatizar copias de seguridad con Python usando pathlib, shutil, fechas, verificación, registros y una estrategia segura de restauración.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026