Resumen
En este artículo implementamos una solución integral para procesar PDFs en cuatro etapas:
- Extracción de texto por página con PyPDF2.
- Limpieza de saltos de línea y normalización con re.
- Traducción del inglés al español usando Ollama y un modelo local (llama3.1:8b).
- Extracción de imágenes incrustadas con PyMuPDF / fitz.
Requisitos
pip install PyPDF2 pymupdf ollama
Además, instala y levanta Ollama y ten disponible el modelo llama3.1:8b.
Estructura de carpetas (resultado)
./paginas/ # Texto por página (crudo)
./paginas_limpias/ # Texto limpio
./paginas_traducidas/ # Texto traducido
./imagen_pdf/ # Imágenes extraídas
Índice
Paso 1 · Extraer texto con PyPDF2
Extraemos el texto de cada página y lo guardamos en archivos .txt independientes dentro de ./paginas.
import PyPDF2
import os
def crearTxt(numero: int, texto: str) -> bool:
"""
Crea un archivo de texto con el contenido dado.
Args:
numero (int): El número de página para el nombre del archivo.
texto (str): El texto a escribir en el archivo.
Returns:
bool: True si el archivo se creó correctamente.
"""
with open(f'./paginas/{numero}.txt', 'w', encoding='utf-8') as f:
f.write(texto)
return True
def extraerTexto(pdf: str) -> str:
"""
Extrae el texto de un archivo PDF y lo guarda en archivos de texto individuales.
Args:
pdf (str): La ruta al archivo PDF.
Returns:
str: El texto concatenado de todas las páginas del PDF.
"""
texto = ''
contador = 1
with open(pdf, 'rb') as f:
pdf_reader = PyPDF2.PdfReader(f)
for page in pdf_reader.pages:
texto_pagina = page.extract_text()
crearTxt(contador, texto_pagina)
texto += texto_pagina
contador += 1
return texto
def main():
"""
Función principal del programa.
Extrae el texto de un PDF y lo guarda en archivos de texto.
"""
pdf = 'a-christmas-carol-charles-dickens.pdf'
extraerTexto(pdf)
if __name__ == '__main__':
main()
Salida esperada: archivos numerados 1.txt, 2.txt, … en ./paginas.
Paso 2 · Limpiar y normalizar texto
Eliminamos saltos de línea problemáticos y conservamos una versión más legible en ./paginas_limpias.
import os
import re
RUTA_ORIGEN = './paginas'
RUTA_DESTINO = './paginas_limpias'
os.makedirs(RUTA_DESTINO, exist_ok=True)
def guardarTexto(texto: str, nombre_archivo: str) -> None:
"""
Guarda el texto en un archivo.
Args:
texto (str): El texto a guardar.
nombre_archivo (str): El nombre del archivo.
"""
with open(os.path.join(RUTA_DESTINO, nombre_archivo), 'w', encoding='utf-8') as f:
f.write(texto)
def limparTexto(texto: str, file: str) -> None:
"""
Limpia el texto eliminando saltos de línea innecesarios y guarda el resultado.
Args:
texto (str): El texto a limpiar.
file (str): El nombre del archivo.
"""
texto = re.sub(r" \n", " ", texto)
guardarTexto(texto, file)
def main():
"""
Función principal del programa.
Recorre los archivos de texto en la ruta de origen, los limpia y los guarda en la ruta de destino.
"""
for root, _, files in os.walk(RUTA_ORIGEN):
for file in files:
if file.endswith('.txt'):
file_path = os.path.join(root, file)
with open(file_path, 'r', encoding='utf-8') as f:
texto = f.read()
limparTexto(texto, file)
if __name__ == '__main__':
main()
Salida esperada: mismos nombres de archivo, pero en ./paginas_limpias.
Paso 3 · Traducir con Ollama
Traducimos línea por línea usando el modelo local llama3.1:8b con Ollama, y guardamos el resultado en ./paginas_traducidas.
import os
import ollama
RUTA_ORIGEN = './paginas_limpias'
RUTA_DESTINO = './paginas_traducidas'
os.makedirs(RUTA_DESTINO, exist_ok=True)
def traducir(texto: str) -> str:
"""
Traduce un texto del inglés al español utilizando el modelo de Ollama.
Args:
texto (str): El texto a traducir.
Returns:
str: El texto traducido.
"""
response = ollama.chat(
model='llama3.1:8b',
messages=[
{
'role': 'user',
'content': 'Te voy a proporciona un texto para traducir del ingles al espanol. Solo traduce. No respondas ni hagas comentarios. El texto es posible que contenga errores o fragmentos de codigo. En ese caso, ignora los mismos.' + texto,
},
],
)
return response['message']['content']
def main():
"""
Función principal del programa.
Recorre los archivos de texto en la ruta de origen, los traduce y los guarda en la ruta de destino.
"""
for root, _, files in os.walk(RUTA_ORIGEN):
for file in files:
texto_a_insertar = ""
if file.endswith('.txt'):
file_path = os.path.join(root, file)
with open(file_path, 'r', encoding='utf-8') as f:
texto = f.readlines()
for linea in texto:
traduccion = traducir(linea)
texto_a_insertar += traduccion
with open(os.path.join(RUTA_DESTINO, file), 'w', encoding='utf-8') as f:
f.write(texto_a_insertar)
if __name__ == '__main__':
main()
Salida esperada: un archivo traducido por cada entrada en ./paginas_traducidas.
Paso 4 · Extraer imágenes con PyMuPDF (fitz)
Exportamos todas las imágenes incrustadas del PDF a .png, guardándolas en ./imagen_pdf.
import fitz
import os
RUTA_DESTINO_IMAGENES = './imagen_pdf'
os.makedirs(RUTA_DESTINO_IMAGENES, exist_ok=True)
def extraerImagenesPdf(pdf: str) -> None:
"""
Extrae las imágenes de un archivo PDF y las guarda como archivos PNG.
Args:
pdf (str): La ruta al archivo PDF.
"""
pdf_document = fitz.open(pdf)
for page_index in range(len(pdf_document)):
page = pdf_document[page_index]
image_list = page.get_images()
for image_index, img in enumerate(image_list, start=1):
xref = img[0]
base_image = pdf_document.extract_image(xref)
image_data = base_image["image"]
if len(image_list) > 1:
nombre_archivo = f"imagen_{page_index}_{image_index}.png"
else:
nombre_archivo = f"imagen_{page_index}.png"
ruta_archivo = os.path.join(RUTA_DESTINO_IMAGENES, nombre_archivo)
with open(ruta_archivo, "wb") as fp:
fp.write(image_data)
def main():
"""
Función principal del programa.
Extrae las imágenes de un PDF específico.
"""
extraerImagenesPdf("Deep Reinforcement Learning with Python (2024).pdf")
if __name__ == "__main__":
main()
Salida esperada: un conjunto de archivos PNG por cada página o imagen encontrada.
Mejoras y buenas prácticas
- Verificar la existencia de las carpetas de salida antes de escribir (ya cubierto con os.makedirs(..., exist_ok=True)).
- Manejar PDFs con páginas que contienen solo imágenes (la extracción de texto puede devolver None).
- Agregar logging y manejo de excepciones para diagnósticos en producción.
- Permitir parámetros por CLI: rutas de entrada/salida, nombre de archivo PDF, modelo de traducción, etc.
- Unir páginas en un solo archivo final si se desea una salida consolidada post-limpieza o post-traducción.