PDF è un formato pensato per la stampa e la visualizzazione, non per l’archiviazione di dati. Proprio per questo è il formato da cui è più difficile estrarre informazioni in modo affidabile tra tutti quelli della nostra panoramica sul parsing di documenti. In PDF arrivano fatture, contratti, specifiche, listini prezzi, estratti conto bancari e documenti della pubblica amministrazione — e quasi sempre bisogna trasformarli in dati strutturati.
Il bivio principale: testo o immagine
La prima cosa che determina l’intera strategia è come il testo è memorizzato nel file. In un PDF «digitale» (generato da Word, da un programma di impaginazione o dal browser) il testo è salvato come caratteri selezionabili — puoi estrarlo direttamente. In un PDF scansionato la pagina è un’immagine: senza riconoscimento ottico (OCR) non contiene caratteri e l’estrazione restituirà il vuoto. Verificarlo è semplice: se nel visualizzatore il testo si seleziona con il mouse, è digitale; altrimenti serve l’OCR. I due casi richiedono strumenti completamente diversi, quindi conviene partire sempre da questa verifica.
Estrazione del testo dai PDF digitali
Python
Per estrarre testo e, soprattutto, tabelle la scelta migliore è pdfplumber. Capisce le coordinate di caratteri e linee, e per questo sa ricostruire la struttura tabellare.
import pdfplumber
with pdfplumber.open("invoice.pdf") as pdf:
page = pdf.pages[0]
text = page.extract_text()
print(text)
for table in page.extract_tables():
for row in table:
print(row) # row è la lista delle celle della rigaQuando serve solo il testo e conta la velocità, si ricorre a PyMuPDF (si importa come fitz) — è velocissimo e rispetta bene la disposizione del testo.
import fitz # PyMuPDF
doc = fitz.open("contract.pdf")
for page in doc:
print(page.get_text())Per compiti semplici, come estrarre il testo pagina per pagina o lavorare con i metadati, basta pypdf (il successore dell’ormai superato PyPDF2).
from pypdf import PdfReader
reader = PdfReader("report.pdf")
for page in reader.pages:
print(page.extract_text())JavaScript / Node.js
In Node la via rapida per ottenere tutto il testo del documento è pdf-parse.
const fs = require("fs");
const pdf = require("pdf-parse");
const buffer = fs.readFileSync("invoice.pdf");
pdf(buffer).then((data) => {
console.log(data.text); // tutto il testo
console.log(data.numpages);
});Quando serve un controllo a basso livello o lavorare nel browser, si usa pdf.js di Mozilla — dà accesso ai singoli frammenti di testo con le loro coordinate.
Java
Nei progetti aziendali è molto diffuso Apache PDFBox. Tieni presente il cambio di API tra le versioni: in PDFBox 3.x il documento si apre con Loader.loadPDF(...), mentre nella 2.x era PDDocument.load(...).
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.File;
try (PDDocument doc = Loader.loadPDF(new File("report.pdf"))) {
String text = new PDFTextStripper().getText(doc);
System.out.println(text);
}Le tabelle: un dolore a parte
In PDF il concetto di «tabella» non esiste: quella che una persona vede come una tabella è in realtà un insieme di blocchi di testo e linee con coordinate. Per questo l’estrazione delle tabelle è sempre un’euristica. pdfplumber le ricostruisce a partire dalle linee della griglia e funziona bene quando la tabella è tracciata. Per le tabelle complesse senza linee, in Python si usano Camelot (metodo basato sulla disposizione) o tabula-py (un wrapper del motore Java Tabula). Se le tabelle sono tante e omogenee, di solito si sceglie uno strumento e lo si calibra su quel layout specifico.
Spesso la cosa più pratica è riversare subito il risultato in una tabella — da lì l’elaborazione prosegue in Excel o CSV, come con qualsiasi fonte tabellare.
Scansioni e OCR
Se il testo non si lascia selezionare, prima di estrarlo bisogna riconoscere la pagina. Lo stack classico in Python: renderizzare le pagine come immagini (con lo stesso PyMuPDF) e riconoscerle con il motore Tesseract attraverso il wrapper pytesseract.
import fitz
import pytesseract
from PIL import Image
import io
doc = fitz.open("scan.pdf")
for page in doc:
pix = page.get_pixmap(dpi=300) # renderizziamo la pagina come immagine
img = Image.open(io.BytesIO(pix.tobytes()))
text = pytesseract.image_to_string(img, lang="ita")
print(text)La qualità dell’OCR dipende molto dalla risoluzione (usa almeno 300 DPI), dalla pulizia della scansione e dalla lingua. Per i documenti in italiano indica sempre il pacchetto lingua ita. Non aspettarti dall’OCR una precisione del cento per cento: il risultato richiede quasi sempre post-elaborazione e revisione.
Quando conviene evitare il parsing diretto del PDF
A volte si scopre che il documento esiste anche in un altro formato: lo stesso estratto è disponibile come esportazione CSV oppure un’API fornisce i dati in JSON. È quasi sempre più affidabile che estrarre una tabella dalla versione da stampa, quindi verifica la fonte prima di buttarti sul PDF.
PDF è un formato in cui la soluzione «universale» non esiste: una fattura, un contratto e una scansione richiedono strumenti diversi. Se ricevi un flusso regolare di documenti omogenei — fatture, documenti di trasporto, specifiche — configureremo l’estrazione dei dati dai tuoi PDF per quel layout specifico, tabelle e OCR delle scansioni inclusi, con il caricamento del risultato nel sistema che ti serve.