Skip to content

stivenson/moodle_scraper

Repository files navigation

Moodle Scraper

📚 LMS Agent Scraper — Sistema de scrapers IA para Moodle

Python LangGraph LangChain Playwright Ollama MCP Moodle Status


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).

📍 Flujo del scraper (mapa)

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
Loading

⚡ Inicio rápido

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 chromium

Copia .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 run

Valores 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.


🛠️ Tecnologías, estándares y protocolos

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).

🚀 Características

  • ✅ 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)

📁 Estructura del Proyecto

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

📤 Salida

El comando python -m lms_agent_scraper.cli run genera:

  1. Reporte Markdown en reports/assignments_report_YYYYMMDD_HHMMSS.md
  2. Archivos de Debug en debug_html/ si está habilitado (SCRAPER_DEBUG_MODE=true en .env)
  3. Mensajes en consola con el progreso y resultados

📊 Formato del Reporte

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

🔧 Solución de Problemas

🔐 Error de Autenticación

Si el login falla:

  1. Verifica PORTAL_BASE_URL, PORTAL_USERNAME y PORTAL_PASSWORD en .env.
  2. Comprueba que la URL del portal sea correcta y revisa debug_html/ si está habilitado.

📭 No se Encuentran Tareas

Si no se encuentran tareas:

  1. Portal con JavaScript: El portal podría usar JavaScript para cargar contenido dinámicamente
  2. Estructura cambiada: El portal podría haber cambiado su estructura HTML
  3. Sin tareas: Realmente no hay tareas en el período consultado
  4. 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/. Con moodle_default puede que no se detecten las tarjetas en algunos portales. Para depurar: SCRAPER_DEBUG_MODE=true y revisar debug_html/courses_page.html.

📝 Notas Importantes

  • ⚠️ Uso Responsable: Este scraper es para uso personal únicamente
  • 🔒 Seguridad: Credenciales en .env. No versionar .env. Ver .env.example y ENV_README.md.
  • 📊 Limitaciones: Depende de la estructura HTML del portal
  • 🔄 Mantenimiento: Puede requerir actualizaciones si el portal cambia

🆘 Soporte

Si encuentras problemas:

  1. Asegúrate de usar python -m lms_agent_scraper.cli run con .env configurado (véase Inicio rápido).
  2. Revisa los archivos de debug en debug_html/ (con SCRAPER_DEBUG_MODE=true en .env).
  3. Verifica dependencias (pip install -e ., playwright install chromium) y credenciales en .env.

🚀 LMS Agent Scraper (v2)

El comando por defecto python -m lms_agent_scraper.cli run es la forma más sencilla de uso (véase Inicio rápido).

📋 Requisitos

  • Python 3.10+
  • pip install -e . y playwright install chromium
  • Variables de entorno en .env (copiar desde .env.example)

⚙️ Configuración (v2)

  1. Copiar .env.example a .env y configurar:

    • PORTAL_PROFILE: perfil YAML (valores iniciales de ejemplo: moodle_unisimon para Universidad Simón Bolívar, Colombia, Aula Extendida; o moodle_default como 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 en debug_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.
  2. 📁 Perfiles YAML en profiles/ definen selectores, auth y opciones por portal (Moodle, Canvas, etc.). El perfil moodle_unisimon es el de ejemplo por defecto (Universidad Simón Bolívar, Colombia, Aula Extendida) e incluye course_discovery para el fallback por contenido.

🔍 Detección de cursos (v2)

En la página "Mis cursos" del portal, la lista de cursos se obtiene en este orden:

  1. 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 con course_link_segments (lista) o course_link_segment (singular) en el perfil; si no se define, se usa por defecto ["course", "courses", "cursos"].
  2. BeautifulSoup (HTML) — respaldo: se parsea el HTML con los selectores del perfil (tarjetas, nombre, enlace; ver esquema del bloque courses más abajo).
  3. 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).
  4. 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).
  5. 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 con max_candidates y candidate_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.

📐 Esquema del bloque courses en el perfil

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.

CampoDescripciónPor defecto (Moodle)
course_link_segmentsLista 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_segmentAlternativa en singular (una sola palabra); se convierte en lista de un elemento.
containerContenedor opcional para acotar la búsqueda (Playwright).[data-region='courses-view']
selectorsLista de selectores CSS para enlaces a curso (Playwright).a[href*='course/view.php']
card_selectorsSelectores que identifican una tarjeta/ítem de curso (BeautifulSoup y detección de presencia).[data-region='course-content'], div.card.course-card
name_selectorsDentro de cada tarjeta, selectores para el nombre del curso (texto o title).a.coursename, span.multiline, [title]
link_selectorDentro de cada tarjeta, selector del enlace al curso.Enlace cuyo href cumple link_href_pattern
link_href_patternPatrón que debe aparecer en el href (ej. course/view, /course/).course/view
fallback_containersSi no hay tarjetas, buscar enlaces dentro de estos contenedores.[data-region='courses-view'], .card-deck, .course-box
more_navigationObjeto opcional para "Ver más" / paginación antes de extraer.No se expande
more_navigation.selectorsLista de selectores (ej. button:has-text('Ver más'), a:has-text('Siguiente')).
more_navigation.expand_before_extractSi true, hacer click en los controles antes de capturar HTML.false
more_navigation.max_clicksNúmero máximo de clicks en "Ver más" / siguiente.0

▶️ Uso (v2)

Comando por defecto (el más sencillo):

python -m lms_agent_scraper.cli run

Usa 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/ -v

🔌 MCP Server (Cursor / Claude Desktop)

En 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.

💻 Desarrollo con Cursor

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.

🧩 Agent Skills (skills.sh)

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 --yes

Ver 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").


📄 Licencia

Este proyecto es para uso educativo y personal únicamente.

About

MCP Server: Scrapers con IA para Moodle: cursos, tareas y reportes. LangGraph, Playwright, Ollama, Agent-Skills.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors