Sistema de scrapers con IA para portales Moodle: extrae cursos, tareas próximas a entregar y genera reportes. Usa perfiles YAML, workflow LangGraph y servidor MCP para cualquier instalación Moodle.
Los valores y el perfil de ejemplo (moodle_unisimon) referencian la Universidad Simón Bolívar (Colombia), ya que su Aula Extendida está en Moodle; puedes usar otros perfiles y URLs para cualquier Moodle.
Forma recomendada de uso: configurar .env (desde .env.example) y ejecutar el comando por defecto (véase Inicio rápido más abajo).
El siguiente diagrama ilustra la interacción entre usuario, agentes y un portal Moodle: se obtienen los cursos y las tareas próximas a entregar (flujo v2: auth → discovery → extracción → reporte).
graph TD
classDef user fill:#c5cae9,stroke:#5c6bc0,stroke-width:2px,color:#283593
classDef agent fill:#fff8e1,stroke:#ffb74d,stroke-width:2px,color:#5d4037
classDef system fill:#e0e0e0,stroke:#757575,stroke-width:2px,color:#424242
User((Usuario / CLI)):::user
Portal["Portal Moodle"]:::system
subgraph ScraperCore
direction TB
Init["Inicialización<br/>.env y perfil YAML"]:::agent
Auth["Autenticación<br/>Login y sesión"]:::agent
Discovery["Course discovery<br/>Mis cursos"]:::agent
Extract["Extracción<br/>Tareas por curso y fechas"]:::agent
Report["Reporte<br/>Markdown / JSON"]:::agent
end
User -->|run| Init
Init -->|config| Auth
Auth -->|POST Login| Portal
Portal -->|Set-Cookie| Auth
Auth -->|sesión| Discovery
Discovery -->|GET Mis cursos| Portal
Portal -->|HTML cursos| Discovery
Discovery -->|lista cursos| Extract
Extract -->|GET cada curso| Portal
Portal -->|HTML tareas| Extract
Extract -->|assignments| Report
Report -->|reporte| User
La forma más sencilla de usar el proyecto es el comando por defecto, que usa los valores de tu .env (perfil, URL del portal, credenciales, días, etc.):
pip install -e .
playwright install chromiumCopia .env.example a .env y configura al menos: PORTAL_PROFILE (perfil YAML, ej. moodle_unisimon o moodle_default), PORTAL_BASE_URL, PORTAL_USERNAME, PORTAL_PASSWORD. Luego:
python -m lms_agent_scraper.cli runValores por defecto: se usa el perfil indicado en PORTAL_PROFILE, los días en SCRAPER_DAYS_AHEAD / SCRAPER_DAYS_BEHIND, y el reporte se escribe en reports/. El perfil moodle_unisimon y las URLs de ejemplo corresponden a la Universidad Simón Bolívar (Colombia), Aula Extendida. Sin argumentos adicionales, este comando es suficiente.
Tabla por categoría de lo usado en la implementación del repo:
| Categoría | Tecnología / Estándar / Protocolo | Uso en el proyecto |
|---|---|---|
| Lenguaje y runtime | Python 3.10+ | Lenguaje principal; tipado y sintaxis modernos. |
| Orquestación | LangGraph | Grafo de estados: auth → discovery → extracción → reporte. Trabajo en conjunto con LangChain → |
| Orquestación | LangChain | Prompts (ChatPromptTemplate), mensajes (HumanMessage), integración con LLMs. Trabajo en conjunto con LangGraph → |
| Automatización y scraping | Playwright | Navegador headless: login, “Mis cursos”, discovery y captura de HTML. |
| Automatización y scraping | BeautifulSoup4 | Parseo HTML: tarjetas de curso, assignments, fechas. |
| Automatización y scraping | lxml | Parser rápido para BeautifulSoup. |
| Automatización y scraping | requests | HTTP para páginas de curso (sesión autenticada). |
| LLM e IA | Ollama | Modelos locales para extracción de cursos y clasificación de páginas. |
| LLM e IA | langchain-ollama | Cliente ChatOllama e integración con LangChain. |
| Configuración | Pydantic / pydantic-settings | Modelos de configuración y validación desde .env (portal, scraper, Ollama). |
| Configuración | python-dotenv | Carga de variables de entorno desde .env. |
| Configuración | PyYAML | Perfiles por portal (selectores, auth, course_discovery) en profiles/. |
| Configuración | Typer | CLI: run, profiles list/validate. |
| Configuración | Rich | Salida enriquecida en terminal (opcional en CLI). |
| Protocolos | MCP (Model Context Protocol) | Servidor FastMCP vía stdio; herramientas get_courses, get_pending_assignments, etc. |
| Protocolos | HTTP/HTTPS | Comunicación con el portal LMS y con Ollama. |
| Protocolos | stdio | Transporte del servidor MCP para Cursor / Claude Desktop. |
| Formatos | YAML | Perfiles de portal y front matter en skills (SKILL.md). |
| Formatos | Markdown | Reportes de tareas y documentación de skills (SKILL.md). |
| Formatos | JSON | Respuestas estructuradas del LLM (cursos, fechas); respuestas MCP. |
| Formatos | HTML | Páginas del portal; parseo con BeautifulSoup y selectores CSS. |
| Estándares y prácticas | SOLID / DRY | Principios de diseño; ver docs/SOLID_AND_QUALITY.md. |
| Estándares y prácticas | Selectores CSS | Perfiles YAML (tarjetas, nombre, enlace, “Ver más”). |
| Desarrollo | pytest / pytest-asyncio | Tests en tests/. |
| Desarrollo | ruff | Linter y formateo (py310, line-length 100). |
| Desarrollo | setuptools / wheel | Empaquetado (pyproject.toml, pip install -e .). |
| Portal objetivo | Moodle (genérico) | Cualquier Moodle; ejemplos: moodle_unisimon (Universidad Simón Bolívar, Colombia). |
- ✅ Autenticación automática en el portal (Playwright)
- 📅 Filtrado de tareas por período personalizable (días adelante/atrás)
- 📊 Generación de reportes en formato Markdown
- 🔍 Modo debug para análisis del portal
- 🛠️ v2: Workflow LangGraph (auth → discovery → extracción → reporte), perfiles YAML, MCP
- 🛠️ v2: Detección de cursos: BeautifulSoup (principal), LLM (Ollama) y Playwright como respaldo; detección de presencia de tarjetas; fallback por contenido (visitar enlaces y clasificar con LLM)
moodle_scraper/
├── pyproject.toml # Paquete instalable: pip install -e .
├── .env.example # Plantilla de variables para v2 (copiar a .env)
├── profiles/ # Perfiles YAML por portal (v2)
│ ├── moodle_unisimon.yml # Perfil de ejemplo (Unisimon, Colombia)
│ ├── moodle_default.yml
│ └── ...
├── src/lms_agent_scraper/ # LMS Agent Scraper (v2)
│ ├── cli.py # Comandos: run, profiles list/validate
│ ├── agents/ # Agentes (login, course discovery, analyzer)
│ ├── graph/ # Workflow LangGraph (nodes, workflow, state)
│ ├── llm/ # Cliente Ollama (extracción y clasificación)
│ ├── tools/ # browser_tools, extraction_tools, report_tools
│ ├── core/ # date_parser, profile_loader, skill_loader
│ ├── skills/ # Prompts LLM en SKILL.md (date-interpreter, course-extractor, etc.)
│ └── mcp/ # Servidor MCP
├── validate_skills.py # Valida que los skills (SKILL.md) carguen correctamente
├── .agents/skills/ # Skills de skills.sh (agent-browser, pdf, webapp-testing, etc.)
├── docs/
│ ├── AGENT_SKILLS.md
│ ├── ARCHITECTURE_VERIFICATION.md
│ └── SOLID_AND_QUALITY.md
├── tests/
└── reports/ # Reportes Markdown generados
El comando python -m lms_agent_scraper.cli run genera:
- Reporte Markdown en
reports/assignments_report_YYYYMMDD_HHMMSS.md - Archivos de Debug en
debug_html/si está habilitado (SCRAPER_DEBUG_MODE=trueen.env) - Mensajes en consola con el progreso y resultados
El reporte incluye:
- 📅 Fecha de generación y período consultado
- 📖 Tareas agrupadas por curso
- ⏰ Fechas de entrega con indicadores de urgencia
- 📝 Descripciones y requisitos de cada tarea
- 🔢 Conteo total de tareas encontradas
Si el login falla:
- Verifica
PORTAL_BASE_URL,PORTAL_USERNAMEyPORTAL_PASSWORDen.env. - Comprueba que la URL del portal sea correcta y revisa
debug_html/si está habilitado.
Si no se encuentran tareas:
- Portal con JavaScript: El portal podría usar JavaScript para cargar contenido dinámicamente
- Estructura cambiada: El portal podría haber cambiado su estructura HTML
- Sin tareas: Realmente no hay tareas en el período consultado
- Perfil de ejemplo (moodle_unisimon): Ese perfil está ajustado para la Universidad Simón Bolívar (Colombia), Aula Extendida. Si usas otro Moodle, crea o usa otro perfil en
profiles/. Conmoodle_defaultpuede que no se detecten las tarjetas en algunos portales. Para depurar:SCRAPER_DEBUG_MODE=truey revisardebug_html/courses_page.html.
⚠️ Uso Responsable: Este scraper es para uso personal únicamente- 🔒 Seguridad: Credenciales en
.env. No versionar.env. Ver.env.exampleyENV_README.md. - 📊 Limitaciones: Depende de la estructura HTML del portal
- 🔄 Mantenimiento: Puede requerir actualizaciones si el portal cambia
Si encuentras problemas:
- Asegúrate de usar
python -m lms_agent_scraper.cli runcon.envconfigurado (véase Inicio rápido). - Revisa los archivos de debug en
debug_html/(conSCRAPER_DEBUG_MODE=trueen.env). - Verifica dependencias (
pip install -e .,playwright install chromium) y credenciales en.env.
El comando por defecto python -m lms_agent_scraper.cli run es la forma más sencilla de uso (véase Inicio rápido).
- Python 3.10+
pip install -e .yplaywright install chromium- Variables de entorno en
.env(copiar desde.env.example)
-
Copiar
.env.examplea.envy configurar:PORTAL_PROFILE: perfil YAML (valores iniciales de ejemplo:moodle_unisimonpara Universidad Simón Bolívar, Colombia, Aula Extendida; omoodle_defaultcomo plantilla genérica). Para otros portales Moodle, usar o crear el perfil correspondiente.PORTAL_BASE_URL,PORTAL_USERNAME,PORTAL_PASSWORD- Opcional:
SCRAPER_DAYS_AHEAD,SCRAPER_DAYS_BEHIND,SCRAPER_MAX_COURSES,SCRAPER_OUTPUT_DIR,SCRAPER_DEBUG_MODE(guardar HTML endebug_html/y más logs) - Opcional (Ollama):
OLLAMA_BASE_URL,OLLAMA_MODEL_NAME,OLLAMA_TEMPERATURE,OLLAMA_NUM_CTX,OLLAMA_NUM_PREDICT— usado para extraer la lista de cursos desde el HTML, clasificar páginas como “curso” en el discovery por contenido y (en el futuro) sugerir selectores. Requiere Ollama en ejecución y un modelo (p. ej.ollama run glm-4.7-flash). Ver ollama.com/library/glm-4.7-flash. Si no está disponible, la extracción se hace con BeautifulSoup y Playwright.
-
📁 Perfiles YAML en
profiles/definen selectores, auth y opciones por portal (Moodle, Canvas, etc.). El perfilmoodle_unisimones el de ejemplo por defecto (Universidad Simón Bolívar, Colombia, Aula Extendida) e incluyecourse_discoverypara el fallback por contenido.
En la página "Mis cursos" del portal, la lista de cursos se obtiene en este orden:
- Enlaces por segmento de URL — método principal: se buscan todos los enlaces cuya URL tenga en algún segmento del path una de las palabras clave configuradas (p. ej.
course,courses,cursos). La comparación es case-insensitive. No depende de clases ni selectores CSS. Se configura concourse_link_segments(lista) ocourse_link_segment(singular) en el perfil; si no se define, se usa por defecto["course", "courses", "cursos"]. - BeautifulSoup (HTML) — respaldo: se parsea el HTML con los selectores del perfil (tarjetas, nombre, enlace; ver esquema del bloque
coursesmás abajo). - LLM (Ollama) — respaldo: si BeautifulSoup no devuelve cursos y Ollama está disponible, se envía un fragmento del HTML al modelo configurado para que devuelva un JSON con la lista de cursos (nombre y URL).
- Playwright — respaldo: primero se prueban todos los enlaces de la página filtrados por las mismas palabras de segmento; si no hay resultados, se usan los locators del perfil (
courses.selectors) dentro del contenedor opcional (courses.container). - Discovery por contenido — fallback opcional (perfil
course_discovery.fallback_when_empty: true): si sigue habiendo 0 cursos, se extraen enlaces candidatos, se visitan y el LLM clasifica si son páginas de curso. Configurable conmax_candidatesycandidate_patterns.
Antes de extraer, se detecta la presencia de tarjetas de curso (detect_courses_presence) y, si el perfil lo indica, se puede expandir "Ver más" / paginación (more_navigation) antes de capturar el HTML.
Todos los campos son opcionales. Si no se definen, se usan valores por defecto compatibles con Moodle (block_myoverview / tarjetas). Así puedes reutilizar el mismo código en otros portales (Canvas, Blackboard, Moodle custom) solo configurando el perfil.
| Campo | Descripción | Por defecto (Moodle) |
|---|---|---|
course_link_segments | Lista de palabras que identifican un enlace a curso si aparecen en algún segmento del path de la URL (case-insensitive). Ej.: ["course", "courses", "cursos"]. | ["course", "courses", "cursos"] |
course_link_segment | Alternativa en singular (una sola palabra); se convierte en lista de un elemento. | — |
container | Contenedor opcional para acotar la búsqueda (Playwright). | [data-region='courses-view'] |
selectors | Lista de selectores CSS para enlaces a curso (Playwright). | a[href*='course/view.php'] |
card_selectors | Selectores que identifican una tarjeta/ítem de curso (BeautifulSoup y detección de presencia). | [data-region='course-content'], div.card.course-card |
name_selectors | Dentro de cada tarjeta, selectores para el nombre del curso (texto o title). | a.coursename, span.multiline, [title] |
link_selector | Dentro de cada tarjeta, selector del enlace al curso. | Enlace cuyo href cumple link_href_pattern |
link_href_pattern | Patrón que debe aparecer en el href (ej. course/view, /course/). | course/view |
fallback_containers | Si no hay tarjetas, buscar enlaces dentro de estos contenedores. | [data-region='courses-view'], .card-deck, .course-box |
more_navigation | Objeto opcional para "Ver más" / paginación antes de extraer. | No se expande |
more_navigation.selectors | Lista de selectores (ej. button:has-text('Ver más'), a:has-text('Siguiente')). | — |
more_navigation.expand_before_extract | Si true, hacer click en los controles antes de capturar HTML. | false |
more_navigation.max_clicks | Número máximo de clicks en "Ver más" / siguiente. | 0 |
Comando por defecto (el más sencillo):
python -m lms_agent_scraper.cli runUsa el perfil y el resto de opciones definidos en .env. No hace falta pasar argumentos; con tener .env configurado basta.
Otros comandos:
# Perfil explícito por línea de comandos (sobrescribe PORTAL_PROFILE del .env)
python -m lms_agent_scraper.cli run --profile moodle_unisimon # perfil de ejemplo, Unisimon
# Listar y validar perfiles
python -m lms_agent_scraper.cli profiles list
python -m lms_agent_scraper.cli profiles validate moodle_unisimon # perfil de ejemplo, Unisimon
# Modo debug: SCRAPER_DEBUG_MODE=true en .env (guarda HTML en debug_html/ y más logs)
# Tests (desde la raíz del repo)
pytest tests/ -vEn la configuración MCP del cliente:
{
"mcpServers": {
"lms-scraper": {
"command": "python",
"args": ["-m", "lms_agent_scraper.mcp.server"],
"env": {
"PORTAL_PROFILE": "moodle_unisimon",
"PORTAL_BASE_URL": "${env:PORTAL_BASE_URL}",
"PORTAL_USERNAME": "${env:PORTAL_USERNAME}",
"PORTAL_PASSWORD": "${env:PORTAL_PASSWORD}"
}
}
}
}🛠️ Herramientas expuestas: get_pending_assignments, get_submitted_assignments, get_courses, generate_report, check_deadlines, list_profiles. El servidor da acceso a portales LMS Moodle; en la configuración de ejemplo se usa el perfil moodle_unisimon (Universidad Simón Bolívar, Colombia). El cliente recibe instructions con ese contexto cuando se usa dicho perfil.
Este proyecto se desarrolla con Cursor. Se usan reglas globales en ~/.cursor/rules/ (aplican a todos los proyectos):
- SOLID y calidad:
solid-and-quality.mdc— principios SOLID, DRY y buenas prácticas. Copia en el repo: docs/SOLID_AND_QUALITY.md. Verificación de cumplimiento: docs/ARCHITECTURE_VERIFICATION.md. - Idioma:
comments-spanish-code-english.mdc— comentarios y docstrings en español; nombres de código en inglés.
MCPs opcionales (instalados en C:\MCPs\ y configurados en ~/.cursor/mcp.json):
| MCP | Descripción |
|---|---|
| pdf-reader | Lee texto de archivos PDF. |
| char-counter | Cuenta caracteres, palabras y líneas en texto o archivo. |
| unused-vars | Analiza código Python y detecta variables/funciones no usadas y variables de entorno referenciadas; opcionalmente compara con un .env. |
Para analizar este repo con unused-vars (y opcionalmente el .env), el agente puede llamar a la herramienta analyze_unused con path al directorio del proyecto y env_file al .env.
Para mejorar el comportamiento de agentes al trabajar con este repo, instala skills desde skills.sh. Se instalan en .agents/skills/ y Cursor (y otros agentes compatibles) los usan automáticamente al trabajar en este repositorio. En docs/AGENT_SKILLS.md encontrarás la lista por prioridad, la tabla labores–skills y la integración con los agentes.
Esenciales: vercel-labs/agent-browser, anthropics/skills (incluye pdf y webapp-testing). Recomendados: browser-use/browser-use, obra/superpowers. Opcionales: wshobson/agents.
npx skills add vercel-labs/agent-browser --yes
npx skills add anthropics/skills --yes
npx skills add browser-use/browser-use --yes
npx skills add obra/superpowers --yesVer docs/AGENT_SKILLS.md para todos los comandos y npx skills list para listar los instalados.
Skills en tiempo de ejecución (prompts del LLM): Los prompts que usa Ollama están en src/lms_agent_scraper/skills/ (archivos SKILL.md por tarea: extracción de cursos, fechas, selectores, etc.). Puedes editarlos sin tocar código. Para validar que carguen bien: python validate_skills.py. Detalle en docs/AGENT_SKILLS.md (sección "Skills en tiempo de ejecución").
Este proyecto es para uso educativo y personal únicamente.
