Contexto y Arquitectura
En el ecosistema actual de la información, plataformas como X (anteriormente Twitter) son minas de oro para descubrir tendencias tecnológicas, lanzamientos de herramientas y tutoriales valiosos. Sin embargo, separar el ruido de la información útil requiere tiempo. ¿El problema? Curar contenido manualmente es tedioso y poco escalable.
Para resolver esto, construí XScrapper (cuyo código completo puedes encontrar en el repositorio oficial: https://github.com/enlabedev/xscraper/), un pipeline automatizado que extrae tweets basados en grupos de hashtags, filtra el contenido utilizando Inteligencia Artificial y envía un newsletter curado por correo electrónico.
Desde una perspectiva técnica, este proyecto es un desafío excelente porque combina:
- Web Scraping moderno y asíncrono lidiando con sitios web dinámicos (SPAs).
- Integración de APIs de Inteligencia Artificial para procesamiento de lenguaje natural.
- Orquestación de procesos con programación de tareas (scheduling).
Arquitectura de Alto Nivel
El sistema está construido en Python 3.11+ y se divide en módulos con responsabilidades únicas:
- Scraper (
src/scraper.py): Utiliza Playwright para la automatización del navegador, permitiendo navegar por X.com, realizar scroll infinito y extraer elementos del DOM. - AI Processor (
src/ai_processor.py): Conecta con modelos LLM a través de OpenRouter y LangChain para resumir y filtrar los tweets con mayor relevancia técnica. - Email Sender (
src/email_sender.py): Genera el HTML del newsletter y lo distribuye utilizando la API de Resend. - Scheduler (
src/scheduler.py): Automatiza las ejecuciones diarias mediante APScheduler.
El Scraper - El Core del Proyecto
¿Por qué Playwright?
A diferencia de herramientas tradicionales como BeautifulSoup (que no ejecuta JavaScript) o Selenium (que suele ser más lento y síncrono por defecto), Playwright ofrece una API asíncrona nativa, manejo impecable de SPAs (Single Page Applications) y una excelente evasión de detección de bots mediante plugins como playwright-stealth.
Inicialización del Navegador
Para navegar en X.com sin ser bloqueados de inmediato, no podemos usar una configuración por defecto. XScrapper inicializa el contexto del navegador inyectando un USER_AGENT realista, configurando el viewport a 1920x1080, estableciendo la localización (es-ES) y añadiendo argumentos específicos (--disable-blink-features=AutomationControlled) para enmascarar que somos un script automatizado. Además, aplicamos stealth_async a la página para ocultar las variables de entorno típicas de navegadores headless.
Manejo de Sesiones con Cookies
El manejo de autenticación puede parecer magia oscura. En lugar de automatizar un "login" escribiendo usuario y contraseña (lo cual suele disparar captchas y bloqueos), exportamos las cookies de una sesión legítima en formato JSON (cookies.json). El scraper carga este archivo y lo inyecta en el contexto del navegador usando await self.context.add_cookies(saved_cookies).
Configuración de las Cookies:
Para raspar X.com de forma exitosa, necesitas exportar las cookies de tu navegador:
- Inicia sesión en X.com en tu navegador (Chrome o Firefox).
- Instala una extensión para gestionar cookies (por ejemplo, "Get cookies.txt" o "Cookie-Editor").
- Exporta las cookies para el dominio
x.comen formato Netscape o JSON. - Guarda el archivo como
cookies.jsonen el directorio raíz del proyecto.
Ejemplo del formato esperado en cookies.json:
[
{
"domain": ".x.com",
"name": "auth_token",
"value": "your_token_here",
"path": "/",
"secure": true,
"sameSite": "Lax"
}
]
El Patrón de Infinite Scroll
X.com carga los tweets de forma dinámica mientras bajas por la página. Para extraer datos, simulamos el comportamiento humano mediante un bucle de scroll:
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await asyncio.sleep(SCROLL_DELAY)
El script verifica la cantidad de tweets cargados en el DOM tras cada scroll, cuenta los "nuevos" mediante un seguimiento de URLs únicas (seen_urls), y se detiene si alcanza el límite objetivo o si detecta que no hay progreso tras múltiples intentos.
Extracción del DOM y Lógica de Negocio
Una vez cargados los elementos, buscamos los artículos con el selector article[data-testid="tweet"]. Extrae el texto, la fecha, y lo más importante: las métricas (likes, retweets, views).
Nuestra lógica de negocio exige que los tweets tengan un mínimo de interacciones (por defecto 10, configurable en hashtags.yaml). Implementa una función _parse_interactions que suma estas métricas, lidiando inteligentemente con sufijos como "K" (miles) o "M" (millones) para convertirlos a enteros comparables.
Lo que ningún tutorial enseña
La mayoría de los tutoriales asumen que la web es un entorno estático y amigable. En producción, la historia es muy distinta.
- Por qué las "sessions" importan: Intentar hacer scraping masivo de X sin autenticación redirige inmediatamente a un muro de login. El uso de un archivo
cookies.jsonpersistente y la inyección silenciosa de cookies es la única forma confiable de mantener acceso a la API interna de búsqueda. - El problema de la detección de bots: X utiliza huellas digitales del navegador para detectar scrapers. Por eso usamos flags como
--disable-dev-shm-usagey--no-sandbox, junto al paqueteplaywright-stealth. Esto elimina las banderas rojas que gritan "¡Soy un robot!". - Estrategias de Rate Limiting: Para evitar ser baneados por hacer demasiadas peticiones, no saltamos de un grupo de hashtags a otro de forma inmediata. Implementamos un
wait_between_requests_ms(por defecto 7000ms o 7 segundos) entre cada grupo, lo que simula una pausa humana.
Testing y Calidad
Probar scrapers es notoriamente difícil porque dependen de interfaces web volátiles de terceros. En XScrapper, abordamos esto aplicando un fuerte enfoque en pruebas unitarias mediante Mocks.
Usamos la librería pytest y pytest-asyncio. En lugar de levantar un navegador real que ralentizaría nuestra suite y causaría flaky tests (pruebas inestables), utilizamos AsyncMock y MagicMock. Simulamos el DOM inyectando objetos falsos que imitan los retornos de los selectores de Playwright.
Establecimos un umbral mínimo de cobertura del 80% (actualmente por encima de este objetivo). Este estándar nos permite refactorizar la lógica de cálculo de métricas o el formato del HTML del email sabiendo que no rompe la lógica central del negocio.
DevOps y Automatización
El código profesional no solo debe funcionar en la máquina local; debe mantener su calidad en el tiempo. Para ello, integramos automatización desde el primer git commit.
-
Pre-commit Hooks: Configuramos
.pre-commit-config.yamlpara ejecutar validaciones automáticamente antes de cada commit. Esto incluye limpieza de espacios en blanco, chequeo de archivos YAML, linting hiper-rápido conruff(yruff-format), escaneo de vulnerabilidades de seguridad conbandit, y un script personalizado (pre_commit_coverage.py) que detiene el commit si la cobertura de tests cae por debajo del 80%. -
Integración Continua (CI): Utilizamos GitHub Actions (
.github/workflows/ci.yml). Ante cualquier Push o Pull Request a las ramas principales, el servidor levanta contenedores de Ubuntu, instala las dependencias de Python 3.11, ejecutaruff, valida tipado estático conmypy, corre los tests conpytesty finalmente sube el reporte de cobertura a Codecov. -
Makefile: Para agilizar el trabajo diario del desarrollador, incluimos comandos como
make lint,make formatymake test.
Prueba y error
Construir un proyecto de inicio a fin como XScrapper expone varias lecciones valiosas:
- Asincronía en Python: Comprender
async/await, el event loop deasyncioy cómo lidiar con peticiones de red simultáneas te separa inmediatamente de los desarrolladores junior promedio. - Reintentar ante todo: Aprenderás que en el scraping, el fallo es la norma. Los selectores cambiarán, la red fallará y los muros de login aparecerán. El uso sistemático de bloques
try/excepty timeouts lógicos es crucial. - La Arquitectura Limpia Paga: Separar el scraper de la IA (
AIProcessor) y del módulo de envío de emails (EmailSender) hace que el código sea testeable y mantenible. Si mañana X.com cierra y decidimos extraer datos de Reddit, la IA y los correos seguirán funcionando intactos. - DevOps Temprano: Acostúmbrate a configurar
ruff,mypyypytestel día 1. Tratar de añadir tipado o linting a un proyecto monolítico heredado es una pesadilla; hacerlo incrementalmente es fácil.
En resumen, los errores y bloqueos son parte del proceso. Crear un web scraper real te saca del "tutorial habitual" y te obliga a lidiar con redes reales, esquemas cambiantes y la suciedad inherente de la web moderna. Al dominar estas herramientas, demuestras que puedes crear valor end-to-end en el mundo real.
Top comments (0)