pipeline.py produce conversione/<stem>/report.json invece di structure_profile.json + report.md. Il JSON contiene tutto: trasformazioni, struttura, distribuzione lunghezze sezioni, anomalie (bare_headers, short/long sections) e residui con esempi. Fix: bare_headers flagga solo header senza corpo < 30 chars; header numerati con corpo lungo (aforismi) non sono anomalie. Nuovo validate.py legge tutti i report.json e stampa tabella di stato per ogni stem (✅ / ⚠️ / ❌) con soglie configurabili. README aggiornato con sezione validazione batch e struttura report.json.
8.2 KiB
conversione — PDF → Markdown pulito
Pipeline automatica che trasforma un PDF grezzo in Markdown strutturato e pronto per la suddivisione in chunk. Gestisce l'intero processo: validazione del PDF, estrazione del testo, pulizia strutturale e analisi della struttura del documento.
Requisiti
Python
pip install opendataloader-pdf pdfplumber
Java 11+
opendataloader-pdf richiede Java sul PATH. Se non è installato:
# Ubuntu / Debian / WSL
sudo apt install default-jdk
# Verifica
java -version
Download alternativo: https://adoptium.net/
Utilizzo
Posiziona il PDF in sources/<nome>.pdf, poi:
# Singolo documento
python conversione/pipeline.py --stem <nome>
# Tutti i PDF in sources/
python conversione/pipeline.py
# Forza la riesecuzione (sovrascrive output esistente)
python conversione/pipeline.py --stem <nome> --force
Il parametro --stem è il nome del file PDF senza estensione.
Esempio: sources/analisi1.pdf → --stem analisi1
Output
Per ogni stem vengono prodotti tre file in conversione/<stem>/:
| File | Descrizione |
|---|---|
raw.md |
Markdown grezzo estratto dal PDF — non modificare |
clean.md |
Markdown pulito e strutturato — input per il chunker |
report.json |
Metriche complete di qualità della conversione |
report.json
Contiene tutto ciò che serve per valutare la conversione: statistiche trasformazioni, struttura rilevata, distribuzione lunghezze sezioni, anomalie e problemi residui con esempi.
{
"stem": "dirittoprivato",
"timestamp": "2026-04-16 15:41",
"transforms": {
"n_accenti_corretti": 0,
"n_dotleader_rimossi": 0,
"toc_rimosso": false,
"n_sezioni_numerate": 63,
"riduzione_pct": 1
},
"structure": {
"livello_struttura": 3,
"n_h1": 0, "n_h2": 6, "n_h3": 163,
"lingua_rilevata": "it",
"strategia_chunking": "h3_aware",
"avvertenze": []
},
"distribution": { "min": 12, "p25": 312, "mediana": 681, "p75": 1197, "max": 6120 },
"anomalie": {
"bare_headers": 0,
"short_sections": 1,
"long_sections": 39,
"bare_headers_list": [],
"short_sections_list": [...],
"long_sections_list": [...]
},
"residui": {
"backtick": 0, "dotleader": 0, "url": 0, "immagini": 0,
"backtick_esempi": []
}
}
strategia_chunking indica come suddividere il documento in chunk:
| Valore | Significato |
|---|---|
h3_aware |
Documento ricco di sezioni ### — usa i ### come boundary |
h2_paragraph_split |
Struttura parziale ## — suddividi per paragrafo dentro ogni ## |
paragraph |
Nessuna gerarchia chiara — suddividi per paragrafo |
sliding_window |
Testo piatto — usa finestra scorrevole |
Validazione batch
Dopo aver convertito uno o più documenti, esegui validate.py per ottenere
una tabella di stato su tutti gli stem:
python conversione/validate.py
Output di esempio:
stem h2 h3 strategia bare corte lunghe backtick dotlead url status
──────────────────────────────────────────────────────────────────────────────────────────────
analisi1 13 279 h3_aware 0 36 151 10 0 0 ⚠️
dirittoprivato 6 163 h3_aware 0 1 39 0 0 0 ✅
nietzsche 4 303 h3_aware 6 104 100 0 0 0 ⚠️
──────────────────────────────────────────────────────────────────────────────────────────────
Totale: 3 ✅ 1 ⚠️ 2 ❌ 0
Legenda colonne:
| Colonna | Significato | Soglia warning |
|---|---|---|
bare |
Header solo-numero senza corpo (### 1. vuoto) |
≥ 1 |
corte |
Sezioni con corpo < 150 chars | informativo |
lunghe |
Sezioni con corpo > 1500 chars | ≥ 80 |
backtick |
Backtick ` residui nel testo |
≥ 1 |
dotlead |
Dot-leader residui (. . . .) |
≥ 1 |
Stato:
- ✅ nessuna anomalia critica
- ⚠️ anomalie presenti, documento processabile ma da verificare
- ❌ struttura non rilevata (
livello_struttura = 0) o > 50 backtick residui
Cosa fa la pipeline
La pipeline esegue quattro fasi in sequenza.
Fase 1 — Validazione
Verifica che il PDF esista, non sia vuoto, non sia protetto da password e contenga testo digitale estraibile. I PDF scansionati (immagini) non sono supportati.
Fase 2 — Estrazione testo
Usa opendataloader-pdf con l'algoritmo XY-Cut++ per ricostruire il
corretto ordine di lettura anche in documenti multi-colonna. Le immagini
vengono ignorate completamente — il clean.md non contiene mai riferimenti
a immagini.
Fase 3 — Pulizia strutturale
Serie di trasformazioni applicate al Markdown grezzo:
| Trasformazione | Problema risolto |
|---|---|
| Rimozione riferimenti immagini | Artefatti ![...]() lasciati dal convertitore |
| Fix accenti backtick LaTeX | `e→è, puo` →può, sar`a→sarà |
| Rimozione dot-leader TOC | - 1.1 Titolo . . . . . 42 (voci indice) |
| Rimozione numerali romani pagina | i, ii, iii su riga isolata (footer LaTeX) |
| Fix header + body concatenati | ### 11 TitoloCorpo testo... → header + paragrafo separati |
| Estrazione header Capitolo inline | Capitolo 3: IL TITOLO nel corpo → ## Capitolo 3: ... |
| Normalizzazione livelli header | ####, ##### → ### (gerarchia uniforme a 3 livelli) |
| Rimozione bold negli header | ## **Titolo** → ## Titolo |
| Normalizzazione ALL-CAPS header | ## IL TITOLO → ## Il titolo |
| Rimozione TOC | Blocchi indice/sommario rilevati per keyword |
| ALL-CAPS standalone → header | Righe in maiuscolo isolate → ## Titolo |
| Sezioni numerate → header | N. Titolo sezione → ### N. + corpo |
| Sezioni con punto → header | - N. Testo aphorismo... → ### N. + corpo |
| Sezioni lista numerate → header | - N Titolo Corpo testo... → ### N. Titolo + corpo |
| Unione paragrafi spezzati | Paragrafi tagliati dal salto pagina PDF ricongiunti |
| Normalizzazione whitespace | Spazi multipli ridotti a singoli |
| Riduzione righe vuote | Tre o più righe vuote consecutive → due |
| Rimozione URL watermark | www.piattaforma.com, https://... su riga isolata |
| Rimozione header senza corpo | Sezioni vuote e header watermark scartati |
Rilevamento automatico tipo documento: se il documento contiene sezioni "Esercizi" (libri di testo accademici), la conversione dei numeri di esercizio in header viene disabilitata automaticamente.
Fase 4 — Analisi struttura
Rileva la gerarchia del documento (conteggio #/##/###), la lingua
(italiano / inglese / sconosciuta), la lunghezza media delle sezioni e
suggerisce la strategia di chunking ottimale. I risultati sono scritti in
structure_profile.json.
Tipi di documento supportati
| Tipo | Esempi | Note |
|---|---|---|
| Testo giuridico / accademico | Manuali, dispense, codici | Header numerati N. e N.N |
| Filosofia / saggistica | Aforismi numerati, capitoli | Pattern - N. testo |
| Matematica / LaTeX | Analisi, algebra, fisica | Fix accenti, TOC, numerali romani |
| Testo generico strutturato | Qualsiasi PDF digitale | Paragrafi e header standard |
Non supportati: PDF scansionati (solo immagini), PDF protetti da password.
Log di esecuzione
Durante l'esecuzione la pipeline stampa le statistiche di ogni trasformazione:
[3/4] Pulizia strutturale...
✅ Immagini rimosse: 0
Accenti corretti: 3701
Dot-leader rimossi: 53
Header concat fixati: 0
TOC rimosso: sì
ALL-CAPS → ##: 14
Sezioni → ###: 279
Paragrafi uniti: 12998
Riduzione testo: 3%
Se un documento è già stato convertito, la pipeline lo salta automaticamente.
Usa --force per rieseguire.