Skip to main content

government-data-pipelines

Patrones para ingerir, estructurar y visualizar datos de fuentes gubernamentales — scraping de webs institucionales, parsers de PDFs con estructura fija, normalización a base de datos, dashboards interactivos. Catálogo de 35+ APIs de datos abiertos españolas (BORME, AEMET, INE, ESIOS, etc.). Incluye casos CIAF (ferroviario), CIAIAC (aviación), CIAIM (marítimo). Patrón Agregador Multi-Fuente para combinar múltiples APIs en dashboards unificados.

설치로 이동

소스 정보

저장소
Ntizar/NtizarBrainMasterMind
최근 소스 활동
2026년 6월 30일 12:46
감지된 SKILL.md 언어
스페인어
스타
2
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
29 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
government-data-pipelines
version
1.4.0
description
Patrones para ingerir, estructurar y visualizar datos de fuentes gubernamentales — scraping de webs institucionales, parsers de PDFs con estructura fija, normalización a base de datos, dashboards interactivos. Catálogo de 35+ APIs de datos abiertos españolas (BORME, AEMET, INE, ESIOS, etc.). Incluye casos CIAF (ferroviario), CIAIAC (aviación), CIAIM (marítimo). Patrón Agregador Multi-Fuente para combinar múltiples APIs en dashboards unificados.
tags
["government","scraping","pdf-parsing","data-ingestion","dashboard","geolocation"]
# Government Data Pipelines — Patrones de ingestión de datos gubernamentales ## Resumen Procedimiento sistémico para convertir documentos públicos gubernamentales (informes, estadísticas, registros) en bases de datos estructuradas + visualizaciones interactivas. Los documentos oficiales españoles suelen tener estructura fija y URLs predecibles. ## Casos de uso ### 1. Informes de la CIAF (ferroviario) - **Fuente:** https://www.transportes.gob.es/organos-colegiados/ciaf - **URLs PDF:** Varios patrones (ver `references/ciaf-scraping.md` — 4 patrones según año) - **Estructura fija:** Resumen → Descripción → Análisis → Conclusiones → Recomendaciones - **Schema:** Ver `templates/ciaf-report-schema.json` en skill `liteparse-document-ai-parsing` - **Scraping:** Ver `references/ciaf-scraping.md` (dentro de este skill) - **⚠️ Disponibilidad:** 2007-2025 están publicados. **277 informes** (ya descargados en `/root/workspace/CIAF/`) + 17 memorias + 7 normativas = 301 PDFs total. **⚠️ PATRÓN DE URL POR AÑOS (CRÍTICO — verificado 2026-06-26):** - **2007-2016:** `/MFOM/LANG_CASTELLANO/ORGANOS_COLEGIADOS/CIAF/INFORMES/YYYY/` - Ej: `/MFOM/LANG_CASTELLANO/ORGANOS_COLEGIADOS/CIAF/INFORMES/2009/` - Los PDFs están en el HTML estático (no AJAX) - Patrón de PDF: `href="(/recursos_mfom/pdf/UUID/ID/FILENAME.pdf)"` (comillas dobles) - Total: ~181 PDFs (2009-2016; 2007-2008 vacíos) - **2017-2025:** `/organos-colegiados/ciaf/informes-finales-de-sucesos-investigados/infofin-YYYY` - Ej: `/infofin-2025` - PDFs en `/recursos_mfom/paginabasica/recursos/XXXX-YY-ZZZZ-if-*.pdf` - Total: ~38 PDFs (ya descargados) - **NO usar filtros GET** (`?field_ciaf_anyo_value=2015`) — no funcionan, solo devuelven menú. - **TOTAL REAL: 277 informes** (ya descargados en `/root/workspace/CIAF/YYYY/` desde 2007 hasta 2025). Distribución: 2007:4, 2008:53, 2009:43, 2010:28, 2011:24, 2012:22, 2013:23, 2014:14, 2015:10, 2016:11, 2017:12, 2018:2, 2019:3, 2020:3, 2021:6, 2022:5, 2023:3, 2024:3, 2025:1. **⚠️ TRES ERAS DE FORMATO (CRÍTICO para el parser):** Los informes tienen 3 formatos distintos según la normativa vigente: 1. **Pre-RD 810/2007 (2007-2008):** Formato libre, secciones variables (Antecedentes/Hechos/Análisis) 2. **RD 810/2007 (2009-2013):** Secciones 1-5 (Resumen, Descripción, Análisis, Conclusiones, Recomendaciones) 3. **RD 623/2014 (2014-2025):** Secciones 0-6 (Abreviaturas, Resumen, Descripción, Análisis, Conclusiones, Recomendaciones, Anexos) - **Normativa de referencia:** `/root/workspace/CIAF/normativa/04-RD_623_2014_ciaf.pdf` — Art. 15 define la estructura obligatoria del informe - **Detectar era por año** antes de parsear. Los 2007-2008 son los más irregulares. **📐 Calidad profesional — requisito mínimo para herramientas de equipo (VERIFICADO 2026-06-26):** Cuando el usuario dice "tengo que enviárselo al equipo y crearles una herramienta que puedan usar ellos", el estándar es **calidad de producción, no prototype**. Checklist mínimo: - **100% de informes con título descriptivo** (no "Informe NN/YYYY") - **0% HTML como texto plano** en el frontend ( `<strong>` se renderiza como bold, no como literal) - **0% etiquetas duplicadas** en el panel de detalle - **Nombres de campos consistentes** entre parser→index→frontend (mismo idioma, mismo nombre) - **Estaciones limpias** (sin frases del PDF como nombre) - **Fechas completas** (sin campos vacíos en informes con PDF fuente) - **Enlaces funcionales** (PDF local + enlace oficial CIAF) Si algún dato es visible para el usuario final, debe ser correcto y presentable. No vale "más o menos" — el usuario lo envía a su equipo y se juega su credibility. **📊 Memorias anuales:** - URL: `/organos-colegiados/ciaf/memorias-anuales/memoriasanuales` - 17 memorias (2008-2024), guardadas en `/root/workspace/CIAF/memorias/` - PDFs en el HTML directamente (no AJAX) — buscar `href='(/recursos_mfom/[^']+\\.pdf)'` - Pattern de PDFs: múltiples rutas (`/recursos_mfom/`, `/recursos_mfom/pdf/UUID/`, `/recursos_mfom/listado/recursos/`) - **NO hay memoria de 2011** — sí existe, pero se saltó en script anterior. Verificar siempre. - **📄 Normativa:** 7 PDFs en `/root/workspace/CIAF/normativa/` — incluye RD 623/2014 que define estructura de informes **📐 Preferencia de arquitectura (verificado):** - **JSON como fuente de verdad** (no MD, no SQLite): GitHub Pages sirve JSON estático - **JSON particionado por año**: `data/reports/YYYY.json` + `data/index.json` ligero (~50KB para 500 registros) - **relations.json** para cruzar entidades × informes × recomendaciones - **Imágenes extraídas** de PDFs con `pdfimages` (poppler) + `pdftoppm` como fallback - **Coherencia** entre memorias anuales e informes individuales — verificar que los datos coinciden - **Auto-import**: pipeline `sync.py` que detecta nuevos PDFs en la web y los procesa automáticamente ### 2. Informes de la CIAIAC (aviación) - **Fuente:** https://www.transportes.gob.es/organos-colegiados/ciaiac - **Mismo patrón** que CIAF pero para accidentes aéreos ### 3. Informes de la CIAIM (marítimo) - **Fuente:** https://www.transportes.gob.es/organos-colegiados/ciaim - **Investigaciones organizadas por año:** `/organos-colegiados/ciaim/investigaciones/2024` ### Arquitectura para datasets grandes (>100 registros, GitHub Pages) Cuando hay 200+ registros, NO usar un solo JSON gigante. Usar **JSON particionado por año**: ``` data/ ├── index.json ← Índice ligero (todos los IDs, metadatos mínimos, ~50KB) ├── reports/ │ ├── 2009.json ← Registros del 2009 │ ├── 2010.json ← Registros del 2010 │ └── ... ├── relations.json ← Entidades × registros × relaciones └── images/ ├── 2009/ │ └── IF-001-2009-fig01.png └── ... ``` **Por qué funciona:** - `index.json` se carga una vez para mapa + filtros (50KB para 500 registros) - `reports/YYYY.json` se carga solo al hacer clic en un registro de ese año - GitHub Pages sirve JSON estático sin backend - Escalable a 500+ registros sin problemas de rendimiento **Por qué NO SQLite en navegador:** - GitHub Pages no sirve SQLite — necesitarías sql.js (500KB) + WebAssembly - Para <1000 registros, JSON con fetch es suficiente - JSON es más fácil de mantener y versionar con git ### Pipeline genérico (paso a paso) ### Paso 1: Descubrimiento de fuentes ```bash # Acceder a la web con curl (NO browser tool — 403 en transportes.gob.es) curl -sL 'https://www.transportes.gob.es/organos-colegiados/ciaf/informes-finales-de-sucesos-investigados' \ -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36' # Extraer enlaces a PDFs curl ... | grep -oP 'href="[^"]*\.pdf"' | sort -u ``` **⚠️ 4 patrones de URL para PDFs en transportes.gob.es:** 1. `recursos_mfom/paginabasica/recursos/XXXX-YY-ZZZZ-if-*.pdf` (2017-2025) 2. `recursos_mfom/pdf/UUID-UUID/UUID/FILENAME.pdf` (2015-2016) 3. `recursos_mfom/YYMMDD-YYMMYY-if-*.pdf` (2015-2016) 4. `recursos_mfom/comodin/recursos/YYMMDDYYMMYYif*.pdf` (2016) **⚠️ 2 patrones de URL por año:** - 2015-2016: `/informes-finales-de-sucesos-investigados/AÑO` - 2017-2025: `/informes-finales-de-sucesos-investigados/infofin-AÑO` - **NO usar filtros GET** (`?field_ciaf_anyo_value=2015`) — no funcionan. ### Paso 2: Scraping masivo - **Frecuencia:** Cron semanal (domingo 06:00 UTC) - **Detección de nuevos:** Comparar hashes de URLs con base de datos local - **Velocidad:** 1 PDF/sec es suficiente para cientos de informes ### Paso 3: Extracción de texto - **Herramienta principal:** `PyMuPDF` (`fitz`) — más fiable que poppler/markitdown, puro Python, sin dependencias del sistema ```python import fitz doc = fitz.open(str(pdf_path)) text = "\n".join(page.get_text() for page in doc) doc.close() ``` - **Fallback:** `markitdown` con extras: `pip install "markitdown[pdf]"` (requiere `pymupdf` como backend de PDF) - **⚠️ `pdftotext` (poppler) frágil en entornos containerizados:** puede fallar por `libpoppler.so.XXX` faltante. Si aparece `No such file or directory: 'pdftotext'`, instalar `libpoppler-dev` O cambiar a PyMuPDF. - **Extracción de imágenes:** `PyMuPDF` también extrae imágenes incrustadas (`page.get_images()` + `doc.extract_image(xref)`). Si no hay imágenes incrustadas, renderizar páginas como PNG con `page.get_pixmap(dpi=150)`. - **OCR:** Si el PDF es imagen (no texto), usar `ocrmypdf` ### Paso 3b: Extracción por páginas vs regex completo (VERIFICADO 2026-06-26) **⚠️ PATRÓN CRÍTICO: Extraer por páginas, NO por regex sobre texto completo.** El enfoque regex sobre texto completo falla porque: - El TOC (índice con puntos `.....39`) se confunde con contenido real - Headers/footers de cada página se mezclan con el contenido - Secciones de diferentes idiomas (inglés al final) contaminan la extracción **Enfoque correcto — extracción por páginas:** ```python def extract_pages(pdf_path: str) -> list[str]: """Extrae texto de cada página por separado.""" doc = fitz.open(pdf_path) pages = [page.get_text() for page in doc] doc.close() return pages def find_section_pages(pages: list[str]) -> dict[int, int]: """Encuentra en qué página empieza cada sección (saltando TOC).""" section_pages = {} for i, text in enumerate(pages): if i < 2: # Skip cover + warning continue if re.search(r'\.{10,}', text): # Skip TOC pages continue for m in re.finditer(r'(?:^|\n)\s*(\d+)\.\s+([A-ZÁÉÍÓÚÑ][A-ZÁÉÍÓÚÑ\s]{5,40})', text): num = int(m.group(1)) if num not in section_pages: section_pages[num] = i return section_pages def get_pages_text(pages, start_page, end_page): """Concatena páginas limpiando headers/footers individuales.""" text = "" for i in range(start_page, min(end_page, len(pages))): page_text = pages[i] # Limpiar headers/footers de CADA página page_text = re.sub(r'Comisión de Investigación de\s*Accidentes Ferroviarios', '', page_text) page_text = re.sub(r'Informe Final de la CIAF\s+\d+/\d{4}', '', page_text) page_text = re.sub(r'^\s*\d{1,2}\s*$', '', page_text, flags=re.MULTILINE) # page numbers page_text = re.sub(r'^.*\.{10,}.*$', '', page_text, flags=re.MULTILINE) # TOC remnants text += page_text + "\n" return text.strip() ``` **Ventajas comprobadas:** - 2024: 3/3 títulos, 3/3 conclusiones, 3/3 recomendaciones (vs 0/3 con regex completo) - 2009: 43/43 títulos, 38/43 conclusiones, 25/43 recomendaciones - Maneja 3 eras de formato automáticamente **Detección de TOC:** Líneas con 10+ puntos consecutivos (`.{10,}`) → saltar página completa **Manejo de bilingüismo:** Los informes 2014+ tienen sección en inglés al final. Cortar extracción de recomendaciones antes de "SAFETY RECOMMENDATIONS" o "English summary": ```python for i in range(start + 1, min(start + 5, len(pages))): if re.search(r'SAFETY\s+RECOMMENDATIONS|English\s+summary', pages[i], re.IGNORECASE): end = i break ``` **Script completo:** `/root/workspace/CIAF-visor/scripts/parse_year_v2.py` — parser funcional con extracción por páginas, geocoding local, y manejo de 3 eras. Ver también: `references/page-based-pdf-extraction.md`, `references/station-coords-geocoding.md`, y `references/ciaf-memoria-parsing.md` (parseo de memorias anuales, diferente de informes individuales). ### Paso 3c: Extracción semántica de campos El mayor reto NO es extraer texto del PDF (PyMuPDF lo hace bien), sino **estructurar el texto libre en campos JSON**. **⚠️ PARADIGMA SHIFT (2026-06):** Para PDFs digitales con texto seleccionable, el enfoque LLM (`pdf-llm-extraction`) supera cualitativamente a los regex: - **LLM:** 270/270 conclusiones (100%), 268/270 recomendaciones (99%), 381 trenes - **Regex:** 194/270 conclusiones (72%), 149/270 recomendaciones (55%), 0 trenes **Usar `pdf-llm-extraction` para batch processing de PDFs digitales.** Los regex de abajo quedan como referencia para entender la estructura de los informes, pero no son la herramienta principal recomendada. Patrones verificados con CIAF (regex, referencia histórica): **Expediente/informe — 4 patrones de título según era (VERIFICADO 2026-06-26):** Los informes CIAF tienen 4 formatos de título distintos. Detectar por orden de prioridad: ```python # Patrón 1 (2007-2008): "Investigación del accidente ferroviario ocurrido en..." m = re.search(r'Investigaci[oó]n del accidente.*?ocurrido.*?(\d{1,2})[/.-](\d{1,2})[/.-](\d{4})', text[:2000]) # Patrón 2 (2015-2018): "CIAF Nº X/XXXX" o "IF X/XXXX" m = re.search(r'(?:CIAF|Informe)\s+(?:N[º°]|n[º°])\s*(\d+/\d{4})', text[:2000]) # Patrón 3 (2019-2021): "expediente nº X/XXXX ocurrido el DD.MM.YYYY"
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기