TechMatch es una plataforma relacional avanzada para la comparación de hardware de computación y laptops. El sistema utiliza bots de web scraping automáticos con Selenium y BeautifulSoup para recopilar especificaciones técnicas de fabricantes y precios reales de retailers locales, consolidando la información en un modelo de datos estructurado que habilita recomendaciones inteligentes y comparaciones homogéneas basadas en perfiles de uso.
- 📊 Catálogo General y Especializado: Clasificación dinámica de productos en múltiples categorías: Laptops, CPUs, GPUs, Memorias RAM y Almacenamiento (SSD/SSD NVMe/HDD).
- 🤖 Ingesta y Validación Estricta: Filtros avanzados en la capa de base de datos que descartan de forma automática registros incompletos (como laptops sin CPU definida) o productos incorrectos (como consolas clasificadas como procesadores).
- 🧠 Comparador Inteligente por Perfiles: Puntuación y recomendación automatizada en base a perfiles de uso:
- 🎮 Gaming: Prioriza frecuencias altas, memoria VRAM de GPUs y latencia baja.
- 💻 Desarrollo de Software: Prioriza núcleos/hilos de CPU y cantidad de RAM.
- 💼 Ofimática/Productividad: Prioriza consumo energético eficiente (bajo TDP) y discos rápidos.
- 🎨 Diseño Gráfico / Creación: Prioriza una mezcla equilibrada de RAM, núcleos y VRAM.
- 🏬 Mapeo de Múltiples Tiendas (N:M): Monitoreo de precios, urls y disponibilidad en retailers reconocidos como Compra Gamer y Mercado Libre.
- 👤 Área de Usuarios: Registro seguro, inicio de sesión flexible (por email o usuario con contraseñas hasheadas con
bcrypt), y gestión de perfil. - 🔑 Recuperación de Contraseña Avanzada: Flujo seguro mediante código OTP (One-Time Password) de 6 dígitos enviado por correo electrónico con expiración automática, temporizador de reenvío de 60 segundos y validación de seguridad de la nueva clave.
- 💾 Historial de Comparaciones: Posibilidad de guardar comparaciones técnicas realizadas, consultar el veredicto en base a perfiles y gestionar el historial (listar y eliminar registros guardados).
- 💖 Gestión de Favoritos: Marcado rápido de componentes de interés para fácil acceso desde el panel de usuario.
- ⏰ Automatización Nocturna: Script
setup_cron.shque configura un cron job para ejecutar los bots automáticamente cada noche a las 3:00 AM.
La solución está separada en capas limpias e independientes:
graph TD
A[PHP Frontend] <-->|Peticiones HTTP JSON| B[Flask Backend API]
B <-->|Patrón Singleton| C[(MySQL DB)]
D[Orquestador run_bots.py] -->|Carga de especificaciones| C
D -->|Scraping de Precios y Enlaces| C
E[setup_cron.sh] -->|Registra en crontab| D
Desarrollado en PHP con Vanilla CSS y JavaScript dinámico. Interactúa de forma asíncrona con el backend:
- Catálogo interactivo con filtrado por categoría, perfil de uso y buscador textual.
- Ficha de detalle especializada por categoría mostrando especificaciones completas y el comparador de precios por tienda.
- Comparador dinámico (
comparar.php): Comparación técnica lado a lado con recomendación inteligente y la opción de guardar la comparación de manera permanente. - Mis Comparaciones (
comparaciones_guardadas.php): Panel exclusivo para usuarios registrados que muestra el historial de comparaciones y permite revivirlas o eliminarlas. - Recuperación de Contraseña (
recuperar.php): Interfaz visual guiada paso a paso (OTP de 6 dígitos y cambio de contraseña con indicador de fortaleza). - Gestión de Sesión segura centralizada en
utils/y persistida de forma nativa en PHP.
Construido en Python con Flask y organizado según una arquitectura en capas:
- Controlador (
app.py): Define las rutas HTTP y gestiona las solicitudes entrantes. - Servicios (
servicios/): Contiene la lógica de negocio (reglas de comparación, autenticación, envío de email y favoritos). - DAO (
dao/): Capa de persistencia dedicada. Implementa consultas seguras a MySQL para las 18 tablas del sistema. - Modelos (
modelos/): Representa las entidades de datos (Laptop, CPU, GPU, RAM, Almacenamiento, Usuario, etc.). - Utils (
utils/): Módulo de normalización de datos scrapeados para su correcta inserción en la base de datos.
Automatización orquestada por run_bots.py dividida en tres fases secuenciales:
- Fabricantes de Laptops: Extrae especificaciones reales y dimensiones físicas directamente desde los portales de ASUS y Lenovo (PSREF).
- Fabricantes de Componentes: Carga características técnicas avanzadas de CPUs oficiales desde los portales de AMD e Intel.
- Retailers: Busca precios e imágenes en Compra Gamer y Mercado Libre, vinculándolos a los modelos del catálogo oficial y mapeando especificaciones sobre la marcha para componentes nuevos mediante expresiones regulares en
normalizacion.py.
TechMatch/
├── setup_cron.sh # Script de configuración automática del cron job
├── backend/ # API REST y Automatización de Scraping
│ ├── app.py # Punto de entrada de la API Flask
│ ├── config.py # Parámetros del sistema y credenciales de BD
│ ├── run_bots.py # Orquestador del scraping diario
│ ├── Requerimientos.txt # Dependencias del backend Python
│ ├── dao/ # Capa exclusiva de Acceso a Datos (MySQL)
│ │ ├── categoria_dao.py # Consultas de categorías de productos
│ │ ├── comparacion_dao.py # Consultas de comparaciones guardadas
│ │ ├── favorito_dao.py # Consultas de productos favoritos
│ │ ├── marca_dao.py # Consultas de marcas registradas
│ │ ├── perfil_uso_dao.py # Consultas de perfiles de uso
│ │ ├── producto_dao.py # Consultas principales de catálogo y detalle
│ │ ├── recuperacion_dao.py # Consultas del flujo OTP de recuperación
│ │ ├── tienda_dao.py # Consultas de tiendas y precios
│ │ └── usuario_dao.py # Consultas de usuarios y autenticación
│ ├── database/ # Manejo de conexión única (Singleton)
│ ├── modelos/ # Clases de dominio de hardware y usuario
│ │ ├── almacenamiento.py # Entidad SSD/HDD
│ │ ├── categoria.py # Entidad Categoría
│ │ ├── comparacion_guardada.py # Entidad historial de comparaciones
│ │ ├── cpu.py # Entidad CPU
│ │ ├── gpu.py # Entidad GPU
│ │ ├── laptop.py # Entidad Laptop
│ │ ├── marca.py # Entidad Marca
│ │ ├── perfil_uso.py # Entidad Perfil de Uso
│ │ ├── producto.py # Entidad base Producto
│ │ ├── ram.py # Entidad RAM
│ │ ├── tienda.py # Entidad Tienda
│ │ └── usuario.py # Entidad Usuario
│ ├── scrapers/ # Scripts de web scraping (Selenium y bs4)
│ │ ├── scraper_base.py # Clase base con lógica compartida de scraping
│ │ ├── scrapers_fabricantes.py # Scraping de especificaciones de laptops (ASUS, Lenovo)
│ │ ├── scrapers_componentes.py # Scraping de CPUs desde AMD e Intel
│ │ ├── scrapers_especificaciones.py # Extracción de especificaciones técnicas detalladas
│ │ ├── recuperador_precios.py # Scraping de precios en retailers (Compra Gamer, ML)
│ │ └── scraper_imagenes.py # Scraping y descarga de imágenes de productos
│ ├── servicios/ # Lógica de negocio
│ │ ├── auth_servicio.py # Autenticación: registro, login y validación de usuarios
│ │ ├── comparacion_servicio.py # Motor de comparación y puntuación por perfiles
│ │ ├── email_servicio.py # Envío de emails con código OTP para recuperación
│ │ ├── favorito_servicio.py # Lógica de gestión de favoritos
│ │ └── producto_servicio.py # Lógica de consulta y filtrado del catálogo
│ └── utils/ # Normalización de títulos y validación cruzada
│ └── normalizacion.py # Expresiones regulares para mapeo de especificaciones
├── frontend/ # Cliente web
│ ├── index.php # Página principal de bienvenida
│ ├── catalogo.php # Grilla de productos con filtros
│ ├── detalle_producto.php # Ficha técnica y ofertas de compra
│ ├── comparar.php # Comparador lado a lado con veredicto y opción de guardar
│ ├── comparaciones_guardadas.php # Panel con el historial de comparaciones del usuario
│ ├── favoritos.php # Listado de productos marcados como favoritos
│ ├── recuperar.php # Interfaz de recuperación de contraseña (OTP)
│ ├── login.php # Inicio de sesión seguro
│ ├── registro.php # Registro de nuevos usuarios
│ ├── componentes/ # Navbar, footer y product cards reutilizables
│ │ ├── navbar.php # Barra de navegación con estado de sesión dinámico
│ │ └── footer.php # Pie de página del sitio
│ ├── config/
│ │ └── api.php # URL centralizada del backend Flask
│ ├── utils/ # Helpers y utilidades PHP del frontend
│ │ ├── session.php # Gestión segura de sesiones (inicio, cierre, validación)
│ │ ├── helpers.php # Funciones generales: sanitización y formato de precios
│ │ ├── set_session.php # Endpoint PHP: sincroniza el login de JS con la sesión PHP
│ │ └── clear_session.php # Endpoint PHP: sincroniza el logout de JS con la sesión PHP
│ └── assets/ # Hojas de estilo y controladores JavaScript
│ ├── css/ # Estilos específicos del sitio
│ ├── js/ # Scripts lógicos del frontend
│ │ ├── api.js # Cliente HTTP centralizado para todas las llamadas a la API
│ │ ├── alertas.js # Sistema de notificaciones y alertas dinámicas
│ │ ├── catalogo.js # Lógica de filtros, búsqueda y renderizado del catálogo
│ │ ├── comparacion.js # Lógica del comparador dinámico y guardado
│ │ ├── comparaciones_guardadas.js # Lógica del historial y eliminación de comparaciones
│ │ └── favoritos.js # Lógica de marcado y gestión de favoritos
│ └── img/ # Recursos visuales e iconos
└── sql/ # Scripts SQL de estructura e inicialización
- Crea una base de datos vacía llamada
techmatch. - Importa el esquema completo del proyecto:
mysql -u tu_usuario -p techmatch < sql/techmatch.sql - Opcionalmente, importa datos iniciales de soporte si necesitas refrescar marcas y categorías:
mysql -u tu_usuario -p techmatch < sql/crear_tablas_nuevas.sql
- Navega al directorio backend:
cd backend - Crea un entorno virtual e instala las dependencias:
python -m venv venv # En Windows venv\Scripts\activate # En Linux / macOS source venv/bin/activate pip install -r Requerimientos.txt
- Revisa la configuración de conexión en
backend/config.py(puedes ajustar el host, puerto, usuario y contraseña de MySQL). - Ejecuta el servidor Flask:
La API correrá por defecto en
python app.py
http://localhost:5000.
- Copia o enlaza la carpeta
frontend/al directorio raíz de tu servidor Apache/Nginx (por ejemplo,www/en Laragon ohtdocs/en XAMPP). - Asegúrate de configurar la URL de tu API backend Flask en
frontend/config/api.php.
Para actualizar precios y poblar nuevos componentes, ejecuta el orquestador principal:
cd backend
python run_bots.pyEl script setup_cron.sh automatiza la configuración del cron job para que los bots se ejecuten automáticamente cada noche a las 3:00 AM sin intervención manual. El script realiza las siguientes acciones:
- Verifica prerrequisitos: Comprueba que existan la carpeta
backend/y el entorno virtual Python (venv/) antes de registrar la tarea. Si alguno falta, aborta con un mensaje descriptivo. - Idempotencia: Antes de agregar la entrada al crontab, verifica si ya existe una tarea para
run_bots.py. Si ya está registrada, lo informa y no crea duplicados. - Registra el cron job: Agrega la siguiente línea al crontab del usuario actual:
0 3 * * * cd /ruta/al/backend && /ruta/al/venv/bin/python run_bots.py >> bots.log 2>&1 - Redirige logs: Toda la salida estándar y los errores del scraping se acumulan en
backend/bots.logpara su revisión posterior.
Para configurarlo en el servidor de producción (Linux/macOS):
# Dar permisos de ejecución al script
chmod +x setup_cron.sh
# Ejecutar el instalador
./setup_cron.shPara verificar que el cron quedó registrado:
# Listar todas las tareas programadas del usuario actual
crontab -l
# Consultar el log de ejecución
tail -f backend/bots.logNota: Antes de ejecutar
setup_cron.shen producción, verifica que la variablePROJECT_DIRal comienzo del script apunte correctamente a tu ruta de instalación.
POST /api/register: Registra un nuevo usuario (emailUsuario,contraseniaUsuario,nombreUsuario).POST /api/login: Inicia sesión devolviendo información del usuario (identificador[usuario o email],contraseniaUsuario).POST /api/recuperar/solicitar: Solicita el inicio de la recuperación de clave. Envía un código OTP de 6 dígitos al correo (emailUsuario).POST /api/recuperar/verificar: Verifica el código OTP ingresado por el usuario (emailUsuario,codigo). Retorna un token temporal de autorización si es válido.POST /api/recuperar/cambiar: Cambia la contraseña por una nueva (emailUsuario,token,nuevaContrasenia).
GET /api/productos: Lista de productos filtrados.- Parámetros opcionales:
categoria(Laptop, CPU, GPU, RAM, Almacenamiento),perfil(Gaming, Desarrollo, Ofimatica, Diseno),busqueda(texto libre),marca(filtro por marca),ordenar(criterio de ordenación).
- Parámetros opcionales:
GET /api/marcas: Lista todas las marcas registradas que poseen productos activos.GET /api/productos/<int:idProducto>: Detalle extendido de un producto específico, incluyendo sus características técnicas específicas por categoría y la lista de precios por tienda.
GET /api/comparar: Compara dos productos de la misma categoría.- Parámetros obligatorios:
idA(ID producto 1),idB(ID producto 2). - Parámetros opcionales:
perfil(Perfil de uso de referencia),idUsuario(para verificar si ya fue guardada).
- Parámetros obligatorios:
POST /api/comparar/guardar: Guarda una comparación técnica en el historial del usuario (idUsuario,idProductoA,idProductoB).GET /api/comparar/historial/<int:idUsuario>: Obtiene el listado de comparaciones guardadas de un usuario.DELETE /api/comparar/<int:idComparacion>: Elimina una comparación guardada por el usuario.
GET /api/favoritos/<int:idUsuario>: Recupera los productos marcados como favoritos por el usuario actual.POST /api/favoritos/agregar: Agrega un producto a la lista de favoritos del usuario (idUsuario,idProducto).DELETE /api/favoritos/eliminar: Remueve un producto de la lista de favoritos del usuario (idUsuario,idProducto).
El módulo implementa un flujo completo y seguro de recuperación de contraseña en tres pasos:
- El usuario ingresa su dirección de correo electrónico en
recuperar.php. - El frontend envía
POST /api/recuperar/solicitaral backend. auth_servicio.pyverifica que el email esté registrado en la base de datos.- Si existe, genera un código OTP aleatorio de 6 dígitos con una validez de 10 minutos, almacenado en la tabla
codigos_recuperacionde la base de datos (gestionada porrecuperacion_dao.py). email_servicio.pyenvía el código al correo del usuario mediante SMTP.
- El usuario ingresa el código OTP de 6 dígitos recibido en su correo.
- Si el código es correcto y no ha expirado, el backend retorna un token temporal de autorización de un solo uso.
- El frontend implementa un temporizador de reenvío de 60 segundos para evitar spam de solicitudes.
- Si el código es incorrecto o ha expirado, se informa al usuario con un mensaje claro.
- Con el token temporal obtenido, el usuario puede definir su nueva contraseña.
- El frontend incluye un indicador de fortaleza de contraseña en tiempo real (mínimo 6 caracteres).
- El backend valida el token temporal antes de proceder. Si es válido, hashea la nueva contraseña con
bcrypty actualiza el registro en la tablausuarios. - El código OTP utilizado queda invalidado una vez completado el proceso.
- Hasheo de Contraseñas: Se utiliza
bcryptpara almacenar contraseñas hasheadas en la tablausuariosde forma segura. - Sesiones PHP Seguras: Centralizadas en
utils/session.php, aplicando directivas contra Session Hijacking y Session Fixation:- Cookies de sesión con flags
HttpOnlyySameSite=Lax. - Regeneración del ID de sesión en cada login exitoso.
- Sesiones que expiran al cerrar el navegador.
- Cookies de sesión con flags
- Sanitización en Frontend:
utils/helpers.phpaplicahtmlspecialchars()a todos los datos dinámicos renderizados en HTML para prevenir ataques XSS.
La carpeta utils/ del frontend centraliza la lógica transversal de seguridad y presentación, evitando duplicación de código entre las distintas páginas PHP.
| Archivo | Rol |
|---|---|
session.php |
Gestión completa del ciclo de vida de la sesión PHP. Expone: session_start_safe(), login_usuario(), logout_usuario(), obtener_usuario_actual(), usuario_autenticado() y requerir_autenticacion(). |
helpers.php |
Funciones de utilidad general: sanitize() (prevención XSS con htmlspecialchars) y format_precio() (formato de pesos argentinos). También expone obtener_url_api() leyendo desde config/api.php. |
set_session.php |
Endpoint interno PHP (POST). Recibe los datos del usuario como JSON desde JavaScript (tras un login exitoso en la API Flask) y los persiste en la sesión PHP nativa mediante login_usuario(). Actúa como puente entre el login JS y la sesión PHP. |
clear_session.php |
Endpoint interno PHP. Destruye la sesión PHP actual invocando logout_usuario(). Es llamado por JavaScript al hacer logout para garantizar que la sesión del servidor también quede limpia. |
Patrón de uso: Cada página PHP incluye
require_once 'utils/session.php'(yutils/helpers.phpdonde aplique) como primera acción, garantizando que la sesión esté disponible y segura desde el inicio de la respuesta.
El sistema permite a los usuarios registrados mantener un registro histórico de sus comparaciones técnicas:
- Estructura de Base de Datos:
comparaciones_guardadas: Cabecera de la comparación vinculada al usuario, fecha y categoría del componente.producto_comparacion: Tabla asociativa N:M que conecta una comparación con exactamente dos productos.
- Procedimientos Almacenados:
sp_guardar_comparacion: Transacciona de forma segura la inserción en cascada de la comparación (cabecera + detalle de los dos productos).sp_eliminar_comparacion: Elimina físicamente en cascada los detalles y la cabecera del historial del usuario.
- Reglas de Integridad (Trigger):
trg_validar_categoria_comparacion: DisparadorBEFORE INSERTenproducto_comparacionque valida a nivel de motor de base de datos que ambos productos de la comparación pertenezcan a la misma categoría, previniendo comparaciones inválidas en la persistencia.
- Flujo en Frontend:
- Desde
comparar.php, si el usuario está logueado se consulta el estado de la comparación actual (usando el parámetroidUsuarioen la petición). Si no está guardada, se presenta un botón para guardar la comparación. - Desde
comparaciones_guardadas.phpse lista dinámicamente el historial (consultando asíncronamente conapi.jsycomparaciones_guardadas.js) y se permite navegar de vuelta al comparador o eliminar la comparación en un clic.
- Desde