Parser de Manga Asíncrono en Python: De la Arquitectura a los Microservicios
Construir un parser de manga escalable es una tarea que combina los desafíos de la programación asíncrona, el diseño de contratos y la gestión de estados. Manga-Day es un ejemplo práctico de la transición de un monolito a una arquitectura de microservicios utilizando asyncio, Pydantic y SQLAlchemy. El proyecto se concibió originalmente como una herramienta para recopilar datos automáticamente de docenas de sitios, pero se ha convertido en un tutorial sobre el diseño de sistemas extensibles.
Fundamentos Arquitectónicos: Contratos y Asincronía
El núcleo del sistema se construye alrededor de la clase abstracta BaseSpider. Cada parser específico (por ejemplo, para multi-manga) implementa una sola interfaz, lo que permite agregar nuevas fuentes dinámicamente sin modificar el núcleo. El contrato incluye tres métodos clave:
from abc import ABC, abstractmethod
class BaseSpider(ABC):
BASE_URL = "https://example-manga.com"
@abstractmethod
async def get(self, url: str, **kwargs) -> Optional[MangaSchema]: ...
@abstractmethod
async def pages(
self, start_page: int | None = None
) -> AsyncGenerator[list[BaseManga], Any]: ...
Este enfoque garantiza:
- Manejo uniforme de errores y tiempos de espera
- La capacidad de probar cada spider de forma independiente
- Soporte para escalado horizontal mediante generadores asíncronos
La asincronía se implementa a nivel de asyncio, lo que permite manejar eficientemente cientos de solicitudes concurrentes sin bloqueos de E/S. Esto es especialmente importante al trabajar con fuentes lentas o inestables.
Validación de Datos y Modelos ORM
Pydantic se utiliza para tipado estricto y validación. El sistema se basa en tres niveles de modelos:
- BaseManga — conjunto mínimo de campos: título, póster, url, sku (hash SHA256)
- MangaWithGallery — agrega una lista de URLs de imágenes
- MangaSchema — modelo completo con géneros, autor e idioma (opcional)
En la base de datos gestionada por SQLAlchemy, se implementan las siguientes entidades:
- Manga, Author, Genre, Language — tablas principales
- GenreManga — relación muchos a muchos para géneros
- Gallery — tabla separada para imágenes (para evitar cargarla con la consulta principal)
- GeneratedPdf — tabla obsoleta, programada para eliminación
Separar la galería en su propia entidad evitó problemas N+1 al cargar listas de manga: las imágenes ahora se cargan solo bajo solicitud explícita.
Gerentes: Lógica de Negocio sobre los Datos
Los gerentes actúan como la capa de lógica de negocio: clases responsables de operaciones específicas:
- MangaManager: operaciones CRUD por SKU, URL, ID
- RequestManager: solicitudes HTTP con lógica de reintentos y tiempos de espera
- AlertManager: sistema universal de notificaciones (Telegram, Discord, etc.)
- SpiderManager: orquestador central para todos los spiders
SpiderManager es particularmente interesante por su modularidad. Se divide en cuatro archivos:
_load.py— carga dinámica de spiders vía__all___status.py— modelos de estado y enums_starter.py— gestión de lanzamiento y ciclo de vida_spider.py— combinación de componentes en una sola interfaz
Esta descomposición simplificó las pruebas y permitió aislar cambios: por ejemplo, agregar un nuevo spider solo requiere registrarlo en __init__.py, sin modificaciones en el núcleo.
Transición a Microservicios: Puntos de Dolor y Soluciones
El proyecto comenzó como un monolito, pero dos servicios fueron catalizadores del cambio:
- PDFService — consumía demasiada RAM, se movió a un contenedor separado
- Bot — requería WebSocket y autenticación JWT, también migrado a un servicio aislado
La migración pasó por varias etapas:
- Frontend desacoplado del núcleo: ahora funciona exclusivamente vía REST API, con Jinja mínimo solo para configuraciones
- Bot reescrito usando aiohttp para WebSocket e implementando acceso basado en roles vía JWT
- Generación de PDF ahora se ejecuta como tarea en segundo plano con uso limitado de memoria
Principales desafíos:
- Serialización de objetos complejos entre servicios
- Consistencia de datos durante fallos parciales
- Configuración de verificaciones de salud y apagado elegante
Lecciones Clave
- Los contratos importan más que la implementación — las clases abstractas permitieron escalar sin refactorizaciones en el núcleo
- Asincronía ≠ rendimiento automático — controla el número de tareas concurrentes y usa semáforos
- ORM no es una bala de plata — la optimización manual de consultas y desnormalización (p. ej., separar la galería) son críticas para el rendimiento
- Los microservicios añaden complejidad pero proporcionan flexibilidad — mover PDFService resolvió el problema de memoria pero aumentó la complejidad de despliegue
- La probabilidad es un resultado arquitectónico — la modularidad de SpiderManager permitió pruebas unitarias sin burlar dependencias externas
El proyecto se ha convertido en un punto de partida para entender cómo construir sistemas que puedan evolucionar durante años. El código ha crecido hasta decenas de miles de líneas, pero la clara separación de responsabilidades lo mantiene mantenible. Los próximos pasos incluyen implementar un broker de mensajes para colas de tareas y CI/CD completo con autotests en cada pull request.
— Editorial Team
Aún no hay comentarios.