Extraer Texto de PDFs en Python, JavaScript y Java
Extrae texto, tablas y metadatos de archivos PDF con Python, JavaScript y Java, incluidos archivos cifrados y documentos escaneados con OCR.
Visión General
Las facturas, contratos e informes siguen llegando en PDF, y no hay una API para consultarlos: tienes que extraer el texto por tu cuenta. PDF es un formato de descripción de página, no de datos: guarda glifos en coordenadas absolutas, así que la extracción nunca es tan limpia como analizar JSON o CSV. Esta receta muestra las librerías que de verdad funcionan para texto, tablas y metadatos en Python, JavaScript y Java, más los dos casos que rompen el parsing ingenuo: archivos cifrados y documentos escaneados.
Cuándo Usar
Usa este enfoque cuando:
- Sacar facturas, recibos o formularios de los adjuntos en PDF.
- Indexar un corpus de PDFs para búsqueda de texto completo.
- Sacar las tablas de informes financieros o artículos de investigación.
- Leer autor y fecha de creación de cada documento para auditarlos o deduplicarlos.
No lo uses cuando los PDFs son escaneos puros sin capa de texto: esos necesitan OCR primero (ver PDFs escaneados y cifrados). Y si el trabajo es crear PDFs en lugar de leerlos, la receta que quieres es Generar Reportes PDF en Python.
Solución
Todos los snippets de abajo funcionan tal cual. Una versión ejecutable está en el repositorio complementario, con un script que genera un PDF de muestra para practicar.
Python
pypdf (el sucesor mantenido de PyPDF2) cubre extracción de texto y metadatos:
# pip install pypdf
from pypdf import PdfReader
reader = PdfReader("document.pdf")
print(f"Páginas: {len(reader.pages)}")
for page in reader.pages:
print(page.extract_text())
Los metadatos viven en el diccionario de información del documento, no en el contenido de las páginas:
reader = PdfReader("document.pdf")
meta = reader.metadata
print(meta.title, meta.author, meta.creation_date)
Para tablas, pdfplumber detecta líneas de separación y alineación del texto:
# pip install pdfplumber
import pdfplumber
with pdfplumber.open("document.pdf") as pdf:
for page in pdf.pages:
for table in page.extract_tables():
for row in table:
print(row)
JavaScript
pdf-parse envuelve PDF.js de Mozilla y devuelve texto más el número de páginas:
// npm install pdf-parse
import pdfParse from "pdf-parse/lib/pdf-parse.js";
import fs from "node:fs";
const dataBuffer = fs.readFileSync("document.pdf");
const data = await pdfParse(dataBuffer);
console.log(data.text);
console.log(`Páginas: ${data.numpages}`);
pdf-lib lee y edita metadatos y campos de formulario:
// npm install pdf-lib
import { PDFDocument } from "pdf-lib";
import fs from "node:fs";
const pdfDoc = await PDFDocument.load(fs.readFileSync("document.pdf"));
console.log(`Páginas: ${pdfDoc.getPageCount()}`);
console.log(pdfDoc.getTitle(), pdfDoc.getAuthor());
Java
Apache PDFBox es el estándar en proyectos JVM:
// Maven: org.apache.pdfbox:pdfbox
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
try (PDDocument doc = PDDocument.load(new java.io.File("document.pdf"))) {
PDFTextStripper stripper = new PDFTextStripper();
System.out.println(stripper.getText(doc));
System.out.println("Páginas: " + doc.getNumberOfPages());
}
Explicación
Una página PDF es una lista de instrucciones de dibujo: “pon el glifo X en la coordenada (72, 540)”. No hay garantía de que el flujo de contenido siga el orden visual de lectura: un artículo a dos columnas suele extraerse entrelazado, línea a línea entre ambas columnas. Un archivo que se renderiza perfecto puede seguir soltando texto desordenado.
Las librerías difieren sobre todo en cuánto análisis de layout hacen sobre los objetos de texto crudos. pypdf concatena bloques de texto en el orden del contenido, suficiente para documentos de una columna. Como pdfplumber se apoya en pdfminer.six, conserva las coordenadas de cada carácter, suficiente para reconstruir palabras, líneas y tablas. pdf-parse delega en PDF.js, el mismo motor que usa Firefox para renderizar PDFs en el navegador. PDFBox ofrece PDFTextStripper para salida plana y PDFTextStripperByArea cuando ya sabes qué región de la página contiene los datos.
Una vez que tienes texto plano o filas fuera del PDF, el trabajo posterior es parsing de datos normal: por ejemplo, alimentar las filas extraídas por el mismo pipeline de Analizar Archivos CSV o normalizar campos con Serializar y Deserializar Datos.
Variantes
| Tecnología | Librería | Mejor para | Notas |
|---|---|---|---|
| Python | pypdf | Texto, metadatos, unir/dividir | Sucesor del deprecado PyPDF2; Python puro |
| Python | pdfplumber | Tablas y extracción con layout | Basado en pdfminer.six; más lento pero preciso |
| Python | pymupdf | Velocidad, imágenes, anotaciones | Motor MuPDF en C; también renderiza páginas |
| Python | pikepdf | Archivos cifrados o dañados | Envuelve qpdf; repara y descifra PDFs |
| JavaScript | pdf-parse | Extracción rápida de texto en Node | Wrapper ligero sobre PDF.js |
| JavaScript | pdf-lib | Metadatos, formularios, modificación | Funciona en Node y en el navegador |
| Java | Apache PDFBox | Pipelines enterprise en JVM | Acceso de bajo nivel; es la base de Tabula |
Elige pdfplumber cuando las tablas son el objetivo, pymupdf cuando importa el rendimiento, y pypdf para todo lo demás. Si el pipeline tiene que descifrar o reparar archivos primero, pon pikepdf delante.
Elegir la Librería Correcta
PDFs Escaneados y Cifrados
Los PDFs escaneados guardan cada página como imagen, así que extract_text() devuelve cadenas vacías aunque una persona vea el texto. Detecta este caso comprobando si la extracción solo produce espacios en blanco y después ejecuta OCR. La vía más fácil es ocrmypdf, que añade una capa de texto buscable al propio archivo:
# Añade una capa de texto oculta; el resultado se analiza como un PDF normal
ocrmypdf entrada-escaneado.pdf salida-busqueda.pdf
En Python, pdf2image junto con pytesseract te da control página a página; en la nube, AWS Textract, Google Document AI y Azure Document Intelligence manejan formularios y escritura a mano mejor que Tesseract en crudo.
Los PDFs cifrados lanzan errores o devuelven cadenas vacías hasta que les pasas la contraseña. Comprueba reader.is_encrypted y llama a reader.decrypt(password) con pypdf; para contraseñas de propietario o archivos dañados, pikepdf es más tolerante:
# pip install pikepdf
import pikepdf
with pikepdf.open("bloqueado.pdf", password="secreto") as pdf:
pdf.save("desbloqueado.pdf") # luego se analiza con pypdf o pdfplumber
Mejores Prácticas
- Haz un muestreo de la calidad de extracción en unas pocas páginas antes de procesar miles de archivos; la precisión varía mucho según la herramienta que generó el PDF.
- Sea cual sea el lenguaje, mantén los readers dentro de bloques
witho try-with-resources para que los handles de archivo y los buffers se liberen de verdad. Los parsers de PDF son codiciosos con ambos. - Para trabajos por lotes, usa
concurrent.futures.ProcessPoolExecutor: la extracción de PDF está limitada por CPU, así que los hilos no ayudan. pdfminer.six(y por tantopdfplumber) puede quedarse colgado con archivos malformados; en producción, ejecútalo en un subproceso con timeout.- Guarda el texto extraído junto a un hash del archivo fuente para reprocesar solo lo que cambió.
Errores Comunes
- Analizar un PDF escaneado y no obtener nada: si
extract_text()devuelve vacío casi siempre significa páginas de solo imagen; envíalas a OCR. - Asumir que el orden visual es el orden de extracción: los layouts de varias columnas mezclan líneas de ambas. Si el orden importa, usa
pdfplumberconlayout=Trueo extrae regiones por bounding box. - La trampa de debug de pdf-parse: importar
pdf-parsedirectamente (en lugar depdf-parse/lib/pdf-parse.js) puede disparar su bloque de test y lanzarENOENTsobretest/data/05-versions-space.pdf. No es un error tuyo: viene de cómo está empaquetado pdf-parse 1.x. - Ignorar el cifrado: llamar a
extract_text()sobre un archivo cifrado lanza errores o no devuelve nada; compruebais_encryptedprimero. - Tratar la salida del OCR como exacta: el OCR confunde
0/O,1/l, celdas fusionadas y símbolos de moneda, así que los importes e identificadores necesitan una validación de formato antes de que los consuma cualquier sistema.
Solución de Problemas
- Texto vacío pero el PDF se ve bien: probablemente el archivo es solo imágenes. Pásale
pdffonts(viene con poppler); si no lista ninguna fuente, no hay capa de texto que leer. - Caracteres corruptos o tildes perdidas: el PDF usa una codificación de fuente personalizada sin mapa ToUnicode; prueba
pymupdfo OCR en su lugar. - Columnas fusionadas en una sola línea: pasa la extracción a
pdfplumbery ajusta sustable_settings, o recorta la región que te interesa conPDFTextStripperByAreaen PDFBox. - El parser se cuelga con un archivo: envuelve la llamada en un subproceso con timeout y mueve el archivo a una carpeta de cuarentena para revisión manual.
Ver También
- Documentación de pypdf: texto, metadatos, cifrado y unión de archivos.
- pdfplumber en GitHub: extracción a nivel de carácter y detección de tablas.
- Apache PDFBox: el toolkit estándar de la JVM para trabajar con PDFs.
- PDF.js: el motor PDF de Mozilla en el navegador, base de
pdf-parse. - ocrmypdf: deja los PDFs escaneados buscables añadiendo una capa de texto sin cambiar su aspecto.
Preguntas frecuentes
¿Cómo instalo un parser de PDF en Python?
Ejecuta pip install pypdf para texto y metadatos, o pip install pdfplumber cuando necesites tablas. Evita PyPDF2: pasó a llamarse pypdf en 2022 y el paquete antiguo ya no recibe actualizaciones.
¿Cuál es la diferencia entre pypdf y pdfplumber?
pypdf extrae texto crudo rápido y maneja metadatos, unión y cifrado básico. pdfplumber analiza las posiciones de los caracteres, así que puede encontrar bordes de tabla y reconstruir filas: úsalo para facturas e informes, aceptando que es más lento.
¿Puedo analizar PDFs en el navegador?
Sí. PDF.js, el motor de Mozilla, corre entero en el cliente y saca el texto página a página; con pdf-lib además puedes leer metadatos y editar formularios sin salir del navegador. Descarga los archivos grandes a un Web Worker para no bloquear el hilo de la interfaz.
¿Por qué el texto extraído sale vacío?
El PDF es casi seguro un escaneo: páginas guardadas como imágenes sin capa de texto. Compruébalo con pdffonts (sin fuentes = sin texto) y pasa OCR con ocrmypdf o un servicio en la nube como Textract antes de extraer.
¿Cómo extraigo tablas de un PDF?
Usa pdfplumber en Python (page.extract_tables()) o camelot para tablas con líneas de rejilla. En Java, los algoritmos de extracción de Tabula corren sobre PDFBox. Para tablas sin bordes, ajusta table_settings con estrategias verticales explícitas como "text".
¿Puedo rellenar o leer campos de formulario PDF?
Sí. pypdf expone los campos AcroForm con reader.get_fields(), y pdf-lib permite leer y rellenar formularios en Node y en el navegador. Los formularios XFA (comunes en PDFs de administraciones públicas) necesitan aplanarse primero; pdftk o PDFBox pueden hacerlo.
Recursos Relacionados
Analizar Archivos CSV
Cómo analizar archivos CSV en Python, Java y JavaScript con ejemplos de código prácticos.
RecipeAnalizar Archivos Excel en Python, JavaScript y Java
Cómo leer y escribir archivos Excel (.xlsx) en Python, JavaScript y Java con openpyxl, SheetJS y Apache POI.
RecipeAnalizar Archivos XML
Cómo analizar documentos XML en Python, Java y JavaScript con ejemplos de código prácticos.
RecipeConvertir JSON a CSV
Cómo convertir datos JSON a formato CSV en Python, Java y JavaScript: aplana objetos anidados, desenrolla arrays y genera salida lista para Excel.
RecipeSerializar y Deserializar Datos
Cómo serializar y deserializar datos en JSON, XML y YAML en Python, Java y JavaScript.
RecipeGenerar Reportes PDF en Python: Guía de ReportLab y fpdf2
Crea documentos PDF con estilos a partir de datos usando ReportLab y fpdf2 en Python.