Mostrando entradas con la etiqueta Textos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Textos. Mostrar todas las entradas

domingo, 19 de abril de 2026

DATOS. Archivos PDF

Bibloteca PyMuPDF (III)

Acceso a pdf. Sólo textos

Aunque PyMuPDF está pensado para extraer (entre otras cosas) el texto del documento con independencia de en qué presentación se encuentre (como vimos en la entrada anterior), cuando en el documento se diferencian bloques de texto (párrafos) y tablas, es posible que deseemos diferenciar unos textos de otros. En este caso vamos a crear un script que da respuesta a esta necesidad.



#0. Importar la biblioteca

import fitz  # PyMuPDF

# 1 Acceder al objeto documento
doc = fitz.open("plataformas_educativas.pdf")

# Generar documento de salida (.txt)
with open("plataformas_txt2.txt", "w", encoding="utf-8") as archivo_txt:
    for pag in doc:
        archivo_txt.write(f"--- Página {pag.number + 1} ---\n")
        
# 1. Localizar las tablas en la página actual
        tabs = pag.find_tables()                     # Busca las tablas presentes en el texto
        tab_rects = [table.bbox for table in tabs]   # Obtenemos los rectángulos que delimitan las tablas

# 2. Obtener los bloques de texto (con independencia de su ubicación)
        blocks = pag.get_text("blocks")              # Cada 'bloque' devuelve (x0, y0, x1, y1, "texto", block_no, block_type)
        for b in blocks:
            rect_bloque = fitz.Rect(b[:4])           # Coordenadas del bloque de texto
            
# 3. Verificar si el bloque está dentro de alguna tabla
            dentro_de_tabla = False                  # Declara la variable para controlar presencia de texto
            for t_rect in tab_rects:
                if rect_bloque.intersects(t_rect):   # Si el bloque de texto toca el área de la tabla...
                    dentro_de_tabla = True           # Cambia el contenido de la variable y...
                    break                            # Se sale del ciclo (y no se "captura" el bloque de texto)
            
# 4. Escribir solo si no es parte de una tabla y es tipo texto (0)
            if not dentro_de_tabla and b[6] == 0:    # Opción contraria a la precedente (if not)
                archivo_txt.write(b[4] + "\n")       # Entonces sí se "capttura" el bloque de texto y se escribe en el .txt (almacen)

        archivo_txt.write("\n\n")                    # Añade salto de línea para diferenciar bloques de texto en el .txt

doc.close()                                          #Cerrar objeto Document

print("Proceso finalizado: Párrafos extraídos omitiendo áreas de tablas.")


Lee detenidamente el script y sus comentarios. Advertirás que la base de su funcionamiento es la identificación de los sectores de texto y de las estructuras de las tablas, ejecutando un proceso opcional, en función del objetivo: capturar (al .txt de saldida) los bloques de texto que no están asociados a la/s tabla/s.

viernes, 17 de abril de 2026

DATOS. Tratamiento de datos

Datos semi-estructurados PDF (II)

De tabla-pdf a txt y a csv

Dado el objetivo de extraer los datos que contienen tablas de archivos .pdf (datos semi-estrucurados), lo normal es aplicar procedimientos que, en teoría, se ajustan al objetivo y no recurrir a la conversión de tabla-pdf a imagen para usar después un OCR. Por eso califiqué antes esta opción como "ultimo recurso", y por eso en esta entrada probaremos a obtener esos datos por otros medios, concretamente mediante la biblioteca PyMuPDF, de la cual ya hablamos en este blog [pendiente enlace a entrada].

Cierto que disponemos de varias opciones para la salida de los datos, pero por claridad expositiva voy a mostrar en primer lugar la que devuelve la mera visualización por pantalla (consola) de los datos que obtenemos con el script. De esta forma podremos observar mejor la lógica del código y las características de lo que nos devuelve.



'''
Este script utiliza la biblioteca PyMuPDF para acceder a un documento pdf, localizar las tablas
que contiene, extraerlo y mostrarlo por pantalla (CMD>
Este script ha sido desarrollado usando IA-Gemini como asistente de programación
'''

#0. Importar biblioteca

import fitz  # PyMuPDF

#Función para acceso y lectura de tablas del pdf------------------------------------

def extraer_tablas_pdf(ruta_pdf):
    
# 1. Abrir el documento pdf

    doc = fitz.open(ruta_pdf)
    
    print(f"Analizando documento: {ruta_pdf}\n")

# 2. Recorrer las páginas del documento...
    for num_pagina, pagina in enumerate(doc):

# ... buscando las tablas en cada página
        tabs = pagina.find_tables()

# Si se encuentra tabla en la página...        
        if tabs:
            print(f"--- Página {num_pagina + 1} ({len(tabs.tables)} tablas encontradas) ---")
#... se recorrer la tabla y...
            for i, tabla in enumerate(tabs.tables):
                
# 3. Se extrae el contenido de la tabla como una lista de listas
                datos = tabla.extract()            
                print(f"\nTabla {i + 1}:")
                for fila in datos:
# Limpiamos saltos de línea internos para facilitar la lectura por pantalla
                    fila_limpia = [str(celda).replace('\n', ' ') if celda else "" for celda in fila]
                    print(fila_limpia)
        else:
# Muestra las páginas que no contienen tablas
                print(f"Página {num_pagina + 1}: No se detectaron tablas.")
        pass
    doc.close()
# Fin de la función-------------------------------------------------------------------------------

# Script de llamada a la función----------------------------------------------------------------

# 4. Configuración de rutas
ruta = "TuArchivo.pdf" # Identificamos el archivo pdf

# 5. Llamada a la función pasando el parámetro (ruta del pdf)

extraer_tablas_pdf(ruta)


Te dejo que leas detenidamente este código para que lo interpretes con ayuda de los comentarios que contiene; también que lo pruebes sobre algun .pdf que contenga tablas, requisito este indispensable. Un uso más centrado en lo funcional que en lo expositivo eliminaría órdenes como print(f"Página {num_pagina + 1}: No se detectaron tablas."), o simplemente las comentaría, que es lo que te yo sugiero; pero aquí cumplen su función, por lo que son necesarias

Y ahora vamos a comprobar qué obtenemos mostrando, a modo de ejemplo, el contenido de la identificada como tabla 2

Al haber transformado el contenido en una lista de listas (como vemos en 3), podemos visualizar cada elemento textiual (indiferenciado entre etiqueta y campo) cada una de las sublistas (líneas de texto) que contiene la gran lista que es el conjunto de los datos obtenidos. Es un avance respecto al mero texto plano, pero no es aun lo deseado, ya que aquí es tratada como igual la etiqueta y el dato.

Vamos a probar con otra salida, la conversión del resultado de la extracción en un archivo .txt, lo que equipara el resultado al obtenido mediente el procedimiento OCR aplicado antes. Además de estudiar el resultado por comparación con el anterior y el referenciado, también podrás comprobar los cambios que conlleva esta propuesta respecto a la mera visualización por pantalla.


'''
Este script utiliza la biblioteca PyMuPDF para acceder a un documento pdf, localizar las tablas
que contiene y extraer sus datos.
Posteriormente se archiva el resultado como .txt
Este script ha sido desarrollado usando IA-Gemini como asistente de programación
'''
#0. Cargar la biblioteca necesaria

import fitz  # PyMuPDF

#Función de acceso a datos y creación de txt------------------------------------------------

def tablas_pdf_a_txt(ruta_pdf, ruta_txt):
# 1. Abrir el documento PDF
    doc = fitz.open(ruta_pdf)
# 2. Creación del archivo txt de salida
    with open(ruta_txt, "w", encoding="utf-8") as archivo_salida:
        archivo_salida.write(f"EXTRACCIÓN DE TABLAS - {ruta_pdf}\n")
        archivo_salida.write("=" * 50 + "\n\n")

        for num_pagina, pagina in enumerate(doc):
# 3. Buscar tablas en la página del pdf
            tabs = pagina.find_tables()
            
            if tabs.tables:
                archivo_salida.write(f"--- PÁGINA {num_pagina + 1} ---\n")
                
                for i, tabla in enumerate(tabs.tables):
                    archivo_salida.write(f"\n[Tabla {i + 1}]\n")
                    
# 4.Extraer los datos de la tabla
                    datos = tabla.extract()
                    
                    for fila in datos:
# Limpiar celdas: quitar saltos de línea y convertir None en vacío
                        fila_limpia = [str(celda).replace('\n', ' ').strip() if celda else "" for celda in fila]                      
# Unir la fila con tabuladores para mantener formato de columnas
                        linea = "\t".join(fila_limpia)
                        archivo_salida.write(linea + "\n")
                    
                    archivo_salida.write("-" * 30 + "\n")
                
                archivo_salida.write("\n")
        
        print(f"Proceso finalizado. Las tablas se han guardado en: {ruta_txt}")

    doc.close()

# Fin de la función-------------------------------------------------------------------------------------------
#Procedimiento de llamada a la función------------------------------------------------------------------------
# 5. Configuración de rutas
pdf_entrada = "mi_archivo.pdf"          # Cambia por tu archivo pdf (sólo el nombre y la extensión)
txt_salida = "tablas_extraidas.txt"     # Cambia por tu nombre de archivo.txt de salida

# 6. Llamada a la función
tablas_pdf_a_txt(pdf_entrada, txt_salida)   


También aquí implementamos alguna información funcionalmente innecesaría (pero que nos sirven para mejorar la comprensión del resultado), lo que distorsiona el logro del objetivo e incrementa el trabajo posterior de adaptación del .txt resultante, pero lo interesante es comprobar que la salida actual ...

... se parece más a la que obtuvimos mediante ocr, de modo que en este caso podemos optar indistintamente por cualquiera de los dos procedimientos, aunque lo esperable es optar por PyMuPDF por resultar menos costoso computacionalmente...

... y menos a la que el mismo script nos ofrece en el CMD y que pudimos ver antes. Las tres, además, se asemejan más a la mera visualización de la tabla (que te muestro debajo) que a lo que implica de estructura de datos, incluyendo la pérdida de referencia al contenido del último de sus cinco (que no cuatro) campos:

Para finalizar esta entrada vamos a modificar la salida anterior y converirla en .csv. Comprobarás que el script es muy parecido al anterior, pero que el resultado es muy diferente. Empecemos por mostrar el script.



'''
Este script utiliza la biblioteca PyMuPDF para acceder a un documento pdf, localizar las tablas
que contiene y extraer sus datos.
Posteriormente se archiva el resultado como .csv, accesible desde un servicio Hoja de cálculo (Excel o Calc)
Este script ha sido desarrollado usando IA-Gemini como asistente de programación
'''
#0. Importar las bibliotecas necesarias

import fitz  # PyMuPDF
import csv

#Función para extraer las tablas y su contenido-----------------------------------

def tablas_a_csv(pdf, archivo_csv_salida):

# 1. Abrir el documento PDF e iniciar variables
    doc = fitz.open(pdf)
    total_paginas = len(doc)
    tablas_encontradas = 0

# 2. Generar el archivo csv de salida
    with open(archivo_csv_salida, mode="w", newline="", encoding="utf-8-sig") as fichero:
        escritor_csv = csv.writer(fichero, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL)    
        
# 3. Recorrer las páginas
        for num_pagina in range(total_paginas):
            pagina = doc[num_pagina]
            tabs = pagina.find_tables() # Buscamos tablas en la página actual
# Si se encuentran tablas en la página actual...
            if tabs.tables:
#... se recorre la tabla y...
                for i, tabla in enumerate(tabs.tables):
                    
# 4. Extraemos el contenido de la tabla
                    datos = tabla.extract()
                    if datos:
                        tablas_encontradas += 1
# Escribimos el encabezado de identificación para la tabla
                        escritor_csv.writerow([f"--- TABLA {tablas_encontradas} (Página {num_pagina + 1}) ---"])  
                        for fila in datos:
# Limpiamos cada celda: quitar saltos de línea y espacios en blanco
                            fila_limpia = [str(celda).replace('\n', ' ').strip() if celda is not None else "" for celda in fila]
                            escritor_csv.writerow(fila_limpia)    
# Añadimos una línea en blanco para separar visualmente las tablas en el CSV
                        escritor_csv.writerow([])
# Generamos mensajes de finalización
    if tablas_encontradas > 0:
        print(f"Éxito: Se han extraído {tablas_encontradas} tablas en '{archivo_csv_salida}'.")
    else:
        print("No se detectaron tablas en ninguna página del documento.")

    doc.close()

#Fin de la función ---------------------------------------------------------------------------------
    
#  Procedimiento de llamada a función-----------------------------------------------------------

# 5. Identificación de rutas
pdf_entrada = "Mi_Archivo.pdf"         # Identificamos el archivo pdf de entrada [Cambia esto por tu archivo pdf (nombre + extensión entre comillas)]
csv_salida = "tablas_extraidas.csv"    # Identificamos el txt de salidad [Pon nombre de tu archivo txt de salida]

# 6. Llamada a la función
tablas_a_csv(pdf_entrada, csv_salida)


En cuanto al script, existen algunas diferencias con el anterior script, empezando por import csv y siguendo por la estructura que genera el archivo .csv (ver código asociado al comentario 2), pero el resto es muy parecido. Lo que no lo es tanto es el resultado.

Al tratarse de un .csv podemos visualizarlo desde Bloc de notas, pudiendo apreciar la similitud que mantiene con la visualización de los datos en pantalla (lista de listas)...

... con el consecuente procedimiento de ajuste y limpieza posterior que implica y que nos devuelve al conocido procedimiento ofimático; pero también podemos acceder a él desde un servicio de Hoja de cálculo (vg. Calc, donde observamos un resultado similar a este...

... que si bien no permite un tratamiento directo como tabla de datos (en el sentido de que no podemos considerarlos aun como datos estructurados), sí nos permite un tratamiento automatizado, cuanto menos para la definitiva conversión o incorporación en una base de datos. En esto proceso de automatización no podemos utilizar directamente una solución como esta, pero sí una similar, aunque para ellos es posible que sea necesario convertir previamente el .csv en .ods (para aplicar un script OOo Basic) o .xlsx (para aplicar un script Python basado en la biblioteca OpenPyXL.

jueves, 16 de abril de 2026

DATOS. Tratamiento de datos

Datos semi-estructurados PDF (I)

Tabla-Imagen

Con frecuencia una de las fuentes de datos estructurados son los documentos de texto, concretamente los documentos PDF. Realmente se trata de una fuente doblemente compleja: son realmente datos estructurados a nuestros ojos, pero precisan un tratamiento manual de tabulación (por ejemplo), pero están muy lejos de serlo para un ordenador, especialmente las "tablas" .pdf.

Antes de seguir y para que no queden dudas respecto al objetivo que se persigue aquí, diré que entiendo por manipulación o tratamiento de datos tabulados en documentos de texto el procedimiento cuyo objetivo último es la creación de estructuras asimilables a una tabla o base de datos que puede ser tratadamediante procedimientos de acceso y consulta a datos estructurados. Digo "objetivo último" porque realmente no siempre se puede garantizar su logro, al menos no de forma directa.

Para un tratamiento ofimático "clásico" no existe diferencia entre un documento .pdf y un documento .docx (o un documento .odt), aunque es posible manipular directamente estos últimos mediante procedimientos relativamente simples basados en los mismos servicios ofimáticos y/o en la combinación procesador-hoja de cálculo. Esta posibilidad se complica si las tablas están en un documento .pdf, pero puede ser una complicación salvable, especialmente si los datos no están sujetos a confidencialidad; si sí lo están va a depender de la suerte que tengamos en el procedo de captura de los datos: si es posible copia-pega, no vamos a tener más trabajo que si se tratara de tablas .docx (o casi), pero si no se dejan capturar de este modo no nos va a quedar más remedio que copiarlos manualmente o casi manualmente.

Pero la automatización se complica significativamente cuando la fuente radica en un documento .pdf, especialmente si estas tablas contienen información confidencial, que no es posible derivar a recursos especialidados, e incluso posiblemente las dificultades persistan en caso de optar por estos medios. De todas formas, en lo que a un SEO se refiere, ni por presupuesto ni por razones de confidencialidad podemos recurrir a ellos medios y la consecuencia es que o bien renunciamos a automatizar el acceso a estas tablas (lo más frecuente) o bien aplicamos procedimientos de automatización que sólo resuelven el problema de forma parcial y sin garantia de estar libres de error.

Deberemos, pues, estar dispuestos a realizar algún tipo de intervención ofimática complementaria como las que se explican en esta entrada. Gran parte de las dificultades se deben a la complejidad que para la digitalización tiene las tablas-pdf, las cuales que presentan diferentes comportamientos no siempre fáciles de predecir ni necesariamente invariables en el mismo documento. Es por ello que las tecnologías de automatización pueden fracasar total o parcialmente, sin que sea garantía de éxito ni la complejidad del recurso de automatización ni que sea actual.

Sobre estas cuestiones trabajaremos en posteriores entradas, pero en esta quiero presentar la opción que se viene a considerar como "el último recurso" y que resuelve parcialmente la automatización del acceso al contenido de las tablas. Me refiero a la conversión de éstas en una imagen y el posterior tratamiento de la imágen mediante técnicas OCR.

Aunque lo más ajustado a un procedimiento de automatización sería convertir automáticamente el .pdf en imagen, el coste de este procedimiento es suficientemente elevado en términos de recursos hardware y computacionales y de memoria como para que no me plantee esta opción como punto de partida, máxime cuando disponemos de un procedimiento tan sencillo como barato: crear manualmente la imagen mediante Imprimir pantalla, procedimiento este perfectamente funcional si la tarea afecta a un pequeño número de documentos. Otra cosa sería si el número de documentos fuera muy elevado y se requiera usar la fotocopiadora como scanner y replantear el trabajo como resposabilidad de equipo. En la imagen que sigue te muestro el resultado (anonimizado) de convertir en imagen la segunda tabla. De paso nos servirá para comprobar qué resulta al aplicar el procedimiento ocr

He de decir que, según IA-Gemnini existen opciones para automatizar esa captura de pantalla, pero no vamos a plantearlas ahora, optando por el procedimiento simple que presuponer un trabajo liviano sobre unos pocos documentos. De este modo nos podremos centrar en automatizar de la conversión de la tabla-imagen a texto mediante el siguiente script:


'''
Acceso a tablas convertidas en img manualmente mediante Imprimir pantalla
El script de Python usa pytesseract como OCR lo que implica tener instalado Tesseract
en el sistema.

Script desarrollado haciendo uso de IA-Gemini para mejoras de funcionamiento
implementadas a demanda específicada por el autor.

****************************************************************************************
--- FASES DEL PROCESO -------------------------
****************************************************************************************
'''

#0. Carga de bibliotecas ---------------------

import pytesseract
from PIL import Image
import os

# 1. Configuraciones y acceso a imagen -------------------------

# Configuración de Tesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

# Configuración de las rutas de salida
nombre_carpeta = 'tablas'
nombre_archivo_txt = 'resultado_ocr.txt'

# Obtener la ruta del directorio donde se ejecuta el script
directorio_actual = os.path.dirname(os.path.abspath(__file__))
ruta_tablas = os.path.join(directorio_actual, nombre_carpeta)

# Crear la carpeta 'tablas' si no existe
if not os.path.exists(ruta_tablas):
    os.makedirs(ruta_tablas)

# Entrada nombre o ruta de img
nombre_imagen = input("Introduce el nombre del archivo de la imagen o su ruta completa (sin comillas): ").strip()

# 2. Procesamiento de OCR -----------------------------------

# Verificamos si el archivo existe antes de continuar
if not os.path.isfile(nombre_imagen):
    print(f"Error: El archivo '{nombre_imagen}' no se encuentra en la ruta especificada.")
else:
    try:
# Iniciamos el procesamiento OCR
        img = Image.open(nombre_imagen)
        texto = pytesseract.image_to_string(img, lang='spa')

# 4. Mostrar y guardar resultado -----------------------------

# Mostrar resultado en consola
        print("\nTexto extraído con éxito:")
        print("-" * 30)
        print(texto)
        print("-" * 30)

# Guardar resultado en archivo TXT usando el nombre de la imagen original para evitar sobreescritura
        nombre_base = os.path.splitext(os.path.basename(nombre_imagen))[0]
        ruta_final_txt = os.path.join(ruta_tablas, f"{nombre_base}.txt")
        
        with open(ruta_final_txt, 'w', encoding='utf-8') as f:
            f.write(texto)
        
        print(f"Archivo guardado en: {ruta_final_txt}")

    except Exception as e:
        print(f"Ocurrió un error al procesar la imagen: {e}")

Los comentarios que acompañan al script, redactados ex-profeso, hacen innecesario su comentario, pero no lo que resulta de su aplicación.

La ventaja que tiene este (que hemos llamado) "último recurso": la conversión de la tabla-pdf en imagen, es que, si la calidad de ésta es buena (y lo debe ser al proceder directamente del texto), el OCR funciona correctamente y transforma el contenido en texto accesible desde Bloc de notas y similares, sin errores ni omisiones. La imagen que sigue muestra la captura de pantalla de ese texto en lo que respecta a la misma tabla 2 que antes mostramos en pdf-img.

Puedes observar que realmente toda la información está ahí: las etiquetas de los campos, los datos, los que sí están (y que aquí ocultamos) y los que no. Ahora bien, esta imagen también muestrar de forma clara, que el resultado se asemeja más a lo que se ve, a la apariencia de lo que se ve que a los datos que muestra: realmente lo obtenido no es una estructura etiqueta-dato, son tres líneas que contienen ciertos textos, sin diferenciar qué papel juega cada uno de ellos, ni reconocer que falta una cuarta línea. Es evidente que estamos aun lejos de haber obtenido una tabla de datos o estructura que se le parezca; llegar a ello va a requerir un tratamiento específico a posteriori.

Esto hace más necesario aun guardar lo obtenido en, por ejemplo, un archivo de texto como .txt que nos permita darle ese tratamiento, al menos de momento mediante recursos ofimáticos, ya que es la forma más básica, cierto, pero también la solución más eficiente, rápida y segura disponible por ahora. Aquí, reitero, explico cómo.

miércoles, 15 de abril de 2026

DATOS. Tratamiento de datos

Datos semi-estructurados

Tratamiento ofimático

Antes de contar con recursos para manejar los datos que hemos llamado semi-estructurados y entre los que figuran aquellos que proceden de tablas insertadas en documentos de texto, todo el proceso se realizaba "manualmente", aunque sería más apropiado decir que mediante procedimientos ofimáticos. Dado que lo que se pretende en esta sub-sección de DATOS Y TEXTOS es automatizar el procedimiento (en la medida de lo posible) es muy conveniente recordar los orígenes, ya que nos van a servir como punto de referencia.

Empecemos por decir que, ya sea en documentos generados con un procesador de texto o en otro en formato .pdf, las primeras tablas con las que hemos trabajado han estado originalmente en este formato, cumpliendo funciones de formulario o como recurso para estructurar la presentación de la información. Esto es importante decirlo, ya que pudiera parecer que en el principio estuvieron las hojas de cálculo, cuando en realidad han sido un recurso incorporado tarde, mál y pobremente, y no pocas veces de forma un tanto obligada o forzada por demandas externas de rendición de cuentas (estadillos pedidos de NEE y documentos similares solicitados por la autoridad, sea esta quien fuera, los cuales no siempre se presentaban en una hoja de cálculo).

Por diversas causas, a veces esos datos contenidos en tablas-Word han terminado formando parte de una tabla excel, proceso que hace tiempo fue ocasional y que actualmente es mucho más frecuente, pero que en cuanto a procedimiento aun sigue sin ser resuelto de forma sistemática, y eso que, salvo que se empleen procedimientos específicos, ofimáticos o basados en script, lo cierto es que, aunque no presenta mayor dificultad, sí consume demasiado tiempo.

Veamos en qué puede consistir el procedimiento manual-ofimático, partiendo de disponer ya de una copia en texto del contenido de las tablas-Word, resultado de aplicar las funcionalidades Seleccionar->Copiar. Aunque caben varias opciones, para evitar que se copien elementos gráficos no deseados, propongo pegar el contenido en un documento .txt (utilidad Bloc de notas, por ejemplo) como segundo paso.

Lo normal es que lo que se copie reproduzca grosso modo la distribución original del texto, lo que no es aceptable en términos de transformación de los datos en datos estructurados. Por eso motivo propongo actuar manualmente sobre el documento .txt para transformar el contenido en una sucesión de líneas de texto etiqueta-dato, lo que supone eliminar muchos espacios en blanco, diferenciar bloques etiqueta-dato que pueden aparentar ser una unidad, separarlos por líneas y hasta transformar algunos contenidos (por ejemplo las "X" que simulan la elección de una (aparente) opción.

Debo decir que si la tabla cuenta con elementos de formulario es posible que esta estrategia no funcione bien, pero de momento vamos a apartar este tipo de situaciones para no complicar innecesariamente la explicación. Por ahora, los controles de formulario nos obligan a reconstruir el contenido manualmente, así que cuantos menos controles de formulario tenga nuestra tabla mejor para nosotros.

También vamos a eludir ahora la necesidad de valorar algunas opciones de estructuración de los datos cuando se plantean varios como opcionales, tomando provisionalmente partido por la opción más simple: mantener sólo la opción elegida y eliminar el resto.

Una vez asumidas estas opciones y organizados los datos según los criterios antes indicados, dispondermos de un documento .txt simple. De uno o de varios, dependiendo de si optamos por tratar toda la información como un único bloque de datos, o diferenciar cada una de las tablas que originalmente presentaba el documento. Y digo esto porque, de optar por la segunda posibilidad considero preferible diferenciar cada bloque-tabla en un archivo .txt diferente y seguir el procedimiento de transformación también de forma diferenciada. Así mantenemos la unidad de procedimiento y eliminamos espacios en blanco inncesarios o diferenciadores de tablas que a la larga terminan creando confusión en el acceso a los datos.

Partiendo en este caso de la unidad de conjunto de los datos, lo que equivale a considerarlos como una tabla única, una vez limpio el documento .txt (el cual guardaremos al menos provisionalmente hasta disponer de la base de datos/tabla Excel), ejecutamos Seleccionar todo-Copiar, creamos un documento word (Writer) y pegamos el contenido copiado antes en el portapapeles.

Fíjate que usamos el procesador de texto y no directamente la hoja de cálculo para evitar posibles errores en la gestión de la información (que se copie todo el texto en una celda o la pareja etiqueta-dato en una misma celda. Si trasladamos los datos al procesador de texto, podremos realizar una manipulación previa al traslado a la hoja de cálculo que evitará esos problemas de gestión. No obstante, es posible que la copia directa del contenido del .txt en Excel-Calc se resuelva satisfactoriamente y se genere automáticamente un listado vertical en el que se diferencian en sendas celdas la etiqueta y el dato.

Si no ese el caso, o si no quieres correr riesgos, lo que deberás hacer es separar TODAS las parejas etiqueta-dato con un caracter, siempre con el mismo (etiqueta:dato, etiqueta,dato...). Con esto suele ser sificiente para que la utilidad de hoja de cálculo interprete que se trata de una estructura campo-contenido (por lo que incluso te puedes ahorrar el uso del procesador de texto), pero de nuevo si quieres asegurar el existo, lo que debes hacer es seleccionar todo el texto y aplicar la utilidad Writer/Word Tablas|Convetir|Texto en tablas, y seleccionar como separador el caracter empleado en la diferenciación etiqueta-dato (por similitud con el formato .csv te sugiero emplear la coma (,) como separador.

Repito, aunque es posible que otras opciones funciones igualmente, este es el procedimiento que te garantiza que todo salga bien. Con él crearás una tabla-registro donde los campos están dispuestos verticalmente, lo que facilita la lectura del registro pero no la creación de una base de datos en la que acumular los diferentes registros que vayamos creando. Este tipo de base de datos se organiza en horizontal, siendo cada columna un campo y cada fila in conjunto de datos o registro. Transformar una en otra es tema para un posterior desarrollo del procedimiento.

martes, 18 de noviembre de 2025

Textos. Python

Aplicación práctica

Extracción de texto a partir de imágenes




Presento en esta entrada la primera aplicación práctica de los procedimientos tratados en esta subsección relativa al trabajo con textos mediante Python. Primero nos situaremos en contexto, después mostraré el script y finalmente lo explicaré.

Me planteo crear una aplicación que facilite la evaluación del grado de adquisición del sistema fonológico. Para ello selecciono la prueba PLON-R Fonología y necesito estudiar su fundamentación teórica, por lo que accedo a su manual. Dado que se ese manual es un documento en papel, caben dos opciones: copiar manualmente la información que considere necesaria o capturarla en imágenes (vg. en png).

Dado que la primera opción lleva cierto tiempo, opto por la segunda, pero para acceder al contenido necesito convertir esas imágenes en texto. Para ello creo un directorio (Textos) donde copio las imágenes ordenadas según la lógica de su contenido y creo un script Python para acceder al texto que contienen las imágenes.

import os
import pytesseract
from PIL import Image

#Ruta a Tesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

'''
Acceder al listado de documentos png del directorio Textos
'''
lista_img = []
#Listado imágenes . Acceso al directorio
directorio = 'Textos/'
os.chdir(directorio)
lista_archivos = os.listdir('.')

for elemento in lista_archivos:
    lista_img.append(elemento)

'''
Acceder a las imágenes para extraer el texto y copiarlo en documento txt
'''
for elemento in lista_img:
    img = Image.open(elemento)
    # Extraer texto
    texto = pytesseract.image_to_string(img, lang ='spa')
    #Escribir el texto en el archivo .txt
    archivo = open('plon_fono.txt','a', encoding='utf-8')
    archivo.write(texto+'\n')
    archivo.close()

Explico a continuación este script que, como puedes ver, requiere trabajar con tres bibliotecas Python:
  • os (import os) para generar el listado del contenido del directorio Textos y automatizar el acceso a esos documentos (imágenes png)
  • Pillow (from PIL import Image) para acceder a cada una de las imágenes
  • Pytesseract (import pytesseract) para trabajar con el programa OCR Tesseract.
En primer lugar establecemos la ruta para acceder al programa OCR (pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'), paso necesario para el procesamiento OCR de las imágenes.

En segundo lugar accedemos al directorio Textos mediante las funciones os y listamos los archivos que contiene como textos para su posterior uso en el bucle de acceso a las imágenes.

Para ello identificamos el directorio (directorio = 'Textos/') y nos ubicamos en él (os.chdir(directorio)) para extraer su contenido (lista_archivos = os.listdir('.')) y pasarlo a una lista (lista_img = []) de string mediante la función append() (lista_img.append(elemento)) dentro de un bucle que recorre el contenido del directorio (for elemento in lista_archivos:). Sobre esa lista de nombres de archivos desarrollaremos la segunda parte del script.

Dicha segunda parte consiste en recorrer los archivos, acceder a su contenido y convertirlo en texto mediante procedimiento OCR. Todo ello se desarrolla dentro de un segundo bucle for (for elemento in lista_img:) que accede secuencialmente a cada imagen usando la librería Pillow (img = Image.open(elemento)) que pasa la imagen la función image_to_string() de la biblioteca pytesseract (que conecta con el programa OCR Tesseract (texto = pytesseract.image_to_string(img, lang ='spa')) que convierte el contenido de la imagen en un texto editable.

Simultáneamente, en el mismo bucle, se desarrolla la tercera fase que consiste en aplicar las funciones de creación, escritura y cierre de documentos externos txt con que cuenta Python.

Esta parte del script repite cíclicamente el proceso de crear/acceder al documento txt (archivo = open('plon_fono.txt','a', encoding='utf-8')) y escribir el contenido obtenido mediante el procedimiento identificado como segunda parte del script...

(texto = pytesseract.image_to_string(img, lang ='spa')) -> (archivo.write(texto+'\n'))

... y finaliza, ya fuera del bucle, con el cierre del documento (archivo.close()).

El resultado de la aplicación de este script es un documento txt, un tanto "desmadejado" para perfectamente útil para incorporar el contenido de esos documentos obtenidos inicialmente como imágenes a nuestro análisis del documento.

Documentos. A efectos de facilitarte el acceso al resultado y la réplica de esta práctica de dejo a continuación algunos materiales que deberás descargar desde este enlace: textos_imagenes y descomprimirlo. Contiene la carpeta Textos, que a su vez contiene unas imágenes y el documento txt resultante (plon_fono.txt), y el script textos_plon.py.


miércoles, 22 de octubre de 2025

Textos. Python.

Textos en imágenes

Obtención del texto (II)



Aunque en la entrada anterior usamos Pillow como programa de procesamiento de imágenes (con una función muy simple, por cierto: cargar la imagen), lo cierto es que lo normal es que se haga uso de OpenCV por su mayor potencia y funcionalidad, así que tendremos que incorporarlo a nuestro arsenal de recursos de automatización del acceso a textos.


Pero lo que quedó patente en la [entrada anterior] es que para lo que lo utilizamos en ese script (plenamente exitoso, por cierto), Pillow es más que suficiente, así que cuando la imagen no presente mayores dificultades y su tratamiento sea tan simple como el realizado, podemos trabajar perfectamente con esa biblioteca con total confianza. 

Otra cosa es que el tratamiento a hacer de la imagen de referencia exija actuaciones de mayor complejidad. En ese caso es posible que OpenCV ofrezca mayor rendimiento, aunque sinceramente tengo mis dudas...

En todo caso lo mejor es que también practiquemos a extraer texto de imágenes usando OpenCV. Y a ello vamos tomando como referencia esta imagen, que en principio se presenta con mayor complejidad gráfica y también con mayor utilidad (obtener el texto de páginas escaneadas de cuentos o de libros de texto o de presentaciones ppt previamente convertidas en capturas de pantalla, por ejemplo)


Este es ahora el script:

import cv2
import pytesseract

#Asociar pytesseract a Tesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

# Cargar imagen
img = cv2.imread("img/img3.png")

# Convertir la imagen a escala de grises
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Aplicar threshold para convertir la imagen a binario
threshold_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Pasar la imagen al progama OCR pytesseract
text = pytesseract.image_to_string(threshold_img, lang='spa')

# Imprimir el texto extraído
print(text)

Puedes apreciar que sustituimos Pillow por OpenCV (import cv2), biblioteca que usamos para cargar la imagen (img = cv2.imread("img/img3.png")), pero también para tratarla a fin de facilitar el escaneo final...
  • Primero la convertimos a escala de grises (gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
  • Y después la convertimos a binario (threshold_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1])
... aunque puedes comprobar que ninguna de las dos instrucciones es estrictamente necesaria y que su uso no cambia sustancialmente el resultado (que realmente es mejorable), pero lo que aquí es poco relevante, en otros contextos puede ser la diferencia entre un buen acceso al texto o un fracaso, así que bueno es saberlo, aunque ahora parezca que nos sobra.

El resto del script (no hay mucho más, pero es importante) es igual que en el script en el que usamos Pillow:
  • La conexión entre Tesseract y pytesseractpytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
  • El escaneo de la imagen (text = pytesseract.image_to_string(threshold_img, lang='spa'))
  • Y la visualización del texto en pantalla (print(text))
También en este caso obviamos el tratamiento del texto y su posible archivo como documento. Ambas tareas quedan pendientes para aplicaciones prácticas reales.

Textos. ocr de Imágenes

OCR. Textos en imágenes

Obtención de textos con Pillow

Pillow (anteriormente conocida como PIL - Python Imaging Library) es la biblioteca estándar para el procesamiento de imágenes en Python. Pillow permite realizar tareas como convertir formatos (pasar de PNG a JPEG, WebP, BMP...), manipular imágenes (cambiar tamaño, recortar, rotar y voltear imágenes) y la mejora de imágenes (ajustar brillo, contraste, color y nitidez). También permite la edición de imágenes: aplicar filtros (desenfoque, realce de bordes), añadir texto y dibujar formas geométricas.

De todo esto hablamos en la sección Medios AV (por lo que no voy a incluir aquí enlaces a la documentación de la biblioteca), pero aquí vamos a hablar de una funcionalidad tal vez marginal para esta biblioteca, pero interesante: su colaboración en script orientados a la recuperación de texto en imágenes.

Ahora te supongo dispuesto a obtener texto alguna imagen con la que te interes trabajar, pero con el software ya correctamente instalado, incluyendo tesseract.exe y las bibliotecas PyTesseract y Pillow. Con todo esto preparado vamos a practicar la extracción de texto de una imágen como la siguiente.

Como ves se trata de una imágen que reproduce un documento de la que pretendemos obtener el texto que contiene para visualizarlo por pantalla, para lo que vamos a emplear este script Python



import pytesseract
from PIL import Image
#Es necesario incluir la ruta a Tesseract para trabajar con pytesseract
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
# Cargar la imagen con Pillow
img = Image.open('img/img_txt.jpg')
# Extraer texto
texto = pytesseract.image_to_string(img, lang ='spa')
#Escribir el texto en pantalla
print(texto)


Como ves es un script muy simple en el que, además del módulo pytesseract también usa la bibioteca Pillow, concretamente su módulo básico (from PIL import Image), necesario para cargar la imagen (img = Image.open('img/img_txt.jpg')). Pero fíjate (y es muy importante) que previamente deberemos incluir una instrucción que comunique pytesseract con Tesseract (existen otras opciones, pero esta es la más sencilla). De no hacerlo no funcionará el script y no obtendremos el texto.

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

Te recomiendo que identifiques la ubicación de Tesseract en tu ordenador (será una dirección muy similar a la mía) y la copies al inicio de tus script.

Esta instrucción texto = pytesseract.image_to_string(img, lang ='spa') es clave en este script, ya que emplea la función pytesseract image_to_string() que requiere dos parámetros, la imagen sobre la que trabajar y el idioma a aplicar (recuerda que en la instalación de Tesseract seleccionaste una serie de idiomas para aplicar OCR), que en nuestro caso es el español (lang ='spa'). Ahora resta visualizar el texto print(texto), aunque en una situación real podríamos necesitar alguna mejora de su legibilidad y posteriormente archivarlo en un documento (txt,por ejemplo).

DATOS. OCR

Textos en imágenes

Obtención de textos mediante OCR

El desafío actual consiste en extraer el texto que contiene una imagén, con independencia de la naturalez de esa imagen, aunque lo más común será un documento fotocopiado y convertido a formato digital, generalmente en formato .pdf. En este caso, aparentemente estamos en la misma situación que ya afrontamos al trabajar con tablas y párrafos de texto .pdf, pero en realidad nos encontramos en la misma situación que ya provocamos como "último recurso" para la extracción de datos de tablas-pdf: lo que tenemos delante no es un texto, es una imagen, por lo que deberemos aplicar técnicas OCR.

Esta situación es bastante frecuente en el manejo de documentos por parte del SEO y afortunadamente el tratamiento mediante OCR es hoy muchos más eficiente que en los principios de la generalización de los medios informáticos, siempre y cuando, claro está, la imagen sea de calidad y el texto legible.

Una de las soluciones Python incorpora un software OCR llamado Tesseract mediante una biblioteca específica de ese lenguaje llamada pytesseract, cuya web oficial tienes en este enlace.

Te recomiendo que te tomes las cosas con calma y que consultes ambas webs antes de proceder a la instalación del software; esto es especialmente necesario para el OCR, ya que su instalación requiere cierto cuidado y debe ser previa a la de la biblioteca de Python. Pero merece la pena el esfuerzo: los resultados son muy satisfactorio y a coste 0.

martes, 30 de septiembre de 2025

Datos. Archivos de texto

Archivos .txt

Lectura de archivos

Para completar el tema del manejo de archivos .txt con Python, nos queda hablar de la lectura o acceso al contenido; cuestión ésta de gran interés para la creación de script funcionales.

Hasta el momento hemos visto la creación y apertura de un archivo y cómo escribir en él, pero desconocemos cómo acceder a su contenido, siendo como es fundamental para el desarrollo de un script de cierta complejidad. Explico a continuación las fases por las que pasa la lectura de un archivo, entendiendo lectura como sinónimo de acceso al contenido:

  • Primero: acceso al archivo, paso previo, común también al proceso de escritura.
  • Segundo: lectura del contenido. Dentro de esta segunda fase deberemos distinguir dos condiciones alternativas: si deseamos acceder al texto como un bloque o si nos interesa hacerlo línea por línea. Esta opción está determinada, fundamentalmente, por el modo en que se ha escrito el contenido en el archivo.

Veamos ahora cómo concretamos en código cada fase y cada opción, ya que todas ellas permiten diferente formulaciones, empezando por el acceso (y apertura) del archivo, acción que permite dos formulaciones: la básica…



archivo = ‘texto.txt’
contenido = open(archivo,’r’, encoding=‘utf-8’)

# Aquí el cuerpo de instrucciones

contenido.close()


… en donde cambiamos el segundo parámetro por 'r’ (reader) para especificar la acción que vamos a aplicar sobre el archivo, y la simplificada, que nos ahora la función de cierre with open(archivo,’r’, encoding=‘utf-8’) as contenido:

La segunda parte del proceso se basa en la lectura del contenido que se realiza, en principio con la función o método read() (en texto_leer = contenido.read()), que almacena en texto_leer toda la información contenida en el archivo. Pero si el contenido el archivo fuera una lista de palabras que ocupara varias líneas (por ejemplo, los días de la semana), podríamos usar el mismo procedimiento, pero obtendríamos un texto único, no una lista de los días de las semana. Si necesitamos acceder a la lista podemos optar por crearla (lista= []) y usar un bucle for para recorrer el archivo abierto añadiendo mediante lista.append()) cada uno de los elementos a la lista; o puedes optar por usar la función readlines() y ahorrarte el bucle. En este segundo caso depende de cómo esté “escrito” el contenido en el archivo.

Empecemos por la forma más simple, suponiendo que el contenido del archivo es un texto formado por uno o varios párrafos y que nuestro interés es usar ese contenido para informar al usuario del script sobre el funcionamiento del mismo. En este supuesto, necesitamos leer el contenido y pasarlo a una variable-cadena que después mostraremos en pantalla. Para ello, sobre texto_lista.txt (listado de días de la semana)…



archivo = ‘texto_lista.txt’
lineas = []
docum = open(archivo,’r’, encoding=‘utf-8’)
lineas = docum.realdlines()
lin = len(lineas)
print(f’Número de elementos de la lista {lin}\n’)
for i in range(lin):
	print(lineas[i].rstrip())

docum.close()    


Este script devuelve una lista de elementos formada por el contenido de cada una de las líneas del documento. La función len() nos permite ver que esa lista consta de 7 elementos y sirve de referencia para limitar el número de ciclos que recorre for i in range(lín). Si quieres confirmar que lineas es una lista y no una cadena de caracteres, sólo tienes que solicitar que se imprima un elemento mediante print(lineas[0]), que te devuelve lunes. Pero lo realmente interesante es el conjunto de posibilidades que te ofrece el uso de lineas como lista.

sábado, 27 de septiembre de 2025

Datos. Archivos de texto

Archivos .txt

Escritura de archivos

Siguiendo con la lógica de la entrada anterior, una vez creado (y abierto) el archivo deberemos traslacar a él los datos generados por nuestro script, procedimiento llamado escritura. Dedicaremos esta entrada a explicar las diferentes formas que tenemos de escribir en un archivo .txt desde un script de Python.

Como sabemos, de las instrucciones que usamos como concreción del segundo parámetro de la función open(), tres nos permiten escribir en el archivo: "x", "w" y "a". Cada una de ellas tiene un comportamiento particular, tanto respecto a la existencia previo (o no) del archivo, como al modo de trabajar con él.

La opción "x" (create) parece ser la más apropiada, pero tiene un inconveniente: si el archivo que pretendemos crear ya existe, devuelve error, por lo que es necesario asegurar que dicho archivo no exista. La opción "w" crea el archivo si éste no existe y no devuelve error, pero borrar lo que contenga ya que sitúa el cursor al inicio del archivo. Por último, la opción "a" funciona igual que la anterior pero no altera el contenido previo del archivo en caso de que éste exista, dado que situa el cursor al final del archivo.

Sabiendo lo anterior podemos optar por la opción que mejor se ajuste a nuestras necesidades. Por ejemplo, si lo que necesitamos es crear un nuevo archivo sabiendo con certeza que no existe, cualquiera de las tres opciones es válida. Aunque "x" parece ser la más específica, en realidad las tres tienen el mismo comportamiento prático, ya que la posición del cursor dentro del archivo (posición de partida para escribir en él) siempre será el inicio del mismo.

Si lo que deseamos es continuar escribiendo en un archivo ya creado, la mejor opción es "a", ya que nos garantiza que se respeta el contenido previo y que el nuevo se escribe a continuación. Pero si nuestro cometido es mantener actualizado y sin redundancias un determinado archivo, la mejor opción es "w", ya que nos garantiza que no se producirá error (como en el caso de "x) si el archivo ya existe, y que el documento siempre estará actualizado.

Vamos a explicar a continuación el proceso mismo de escritura, empezando por utilizar la función write() en su forma de uso más simple. Un ejemplo...



archivo = open('texto1.txt','x', encoding='utf-8')
archivo.write('Pedro como plátanos')
archivo.close()


... y una variación sobre el mismo tema, sustituyendo la cadena por la variable que la referencia...


frase ='Pedro come plátanos'
archivo = open('texto1.txt','x', encoding='utf-8')
archivo.write(frase)
archivo.close()


... y una tercera formulación, más sofisticada e interactiva, pero en lo principal, la misma:


nombre = input('Dime el nombre de la persona: ')
verbo = input(f'¿Qué hace {nombre }? ')
objeto = input(f'¿Qué {verbo} {nombre} ')
frase = (f'{nombre} {verbo} {objeto}')		#Concetana el contenido de las tres variables

archivo = open('texto1.txt','x', encoding='utf-8')
archivo.write(frase)
archivo.close()


También podemos trabajar con colecciones de datos y con bucles, previamente a la escritura del contenido en el archivo y en proceso de escritura. Pensemos, por ejemplo, en una lista de vocabulario básico que almacenamos inicialmente en una lista y que después escribimos en un archivo.



frutas=['Pera','Plátano','Manzana','Naranja','Fresa']
archivo = open('texto1.txt','x', encoding='utf-8')
for fruta in frutas:
	archivo.write(fruta)
archivo.close()


Aunque este script nos permite crear, acceder y escribir en el archivo texto1.txt, el problema que nos encontramos al acceder al contenido del archivo es el siguiente: "PeraPlátanoManzanaNaranjaFresa". Todas las palabras se escribieron juntas, en una misma línea, cosa que normalmente no es lo que se espera. Para evitar este problemas y que el resultado sea una lista legible, inlcuso que se escriban en líneaa diferentes, deberemos plantearnos otras formulaciones.

  • Por ejemplo, si usamos la siguiente instrucción archivo.write(' ') tras archivo.write(fruta) obtendremos "b>Pera Plátano Manzana Naranja Fresa". La razón es que tras cada elemento de la lista escribimos un espacio mediante esa nueva instrucción.
  • Siguiendo este procedimiento, y manteniendo la escritura del listado en una misma línea, podemos añadir cualquier carácter o grupo de caracteres para separar cada elemento de la lista, como, por ejemplo sustituyendo archivo.write(' ') por archivo.write('||'), con lo que obtendremos "Pera||Plátano||Manzana||Naranja||Fresa||".
  • Estarás pensando que si usas el carácter salto de línea es posible escribir la lista en diferentes líneas (archivo.write('\n')). Y estarás en lo cierto.
Pues bien, el mismo efecto que obtenemos con el bucle for fruta in frutas: lo obtenemos usando join() como te muestro en el siguiente script:


frutas=['Pera','Plátano','Manzana','Naranja','Fresa']
archivo = open('texto1.txt','x', encoding='utf-8')
texto = '\n'.join(frutas) #Si no queremos saltos de línea podemos usar, por ejemplo '||'
archivo.write(texto)
archivo.close()


... con el que conseguimos el mismo efecto que con la anterior formulación del script con bucle.

Cuando el texto a guardar es de varias líneas, podemos usar el método writelines() en sustitución de writer(). También este método nos ahorra el uso del bucle, ya que escribe las múltiples líneas de las que puede estar compuesto un texto o los diferentes elementos de una lista...



frutas=['Pera','Plátano','Manzana','Naranja','Fresa']
archivo = open('texto1.txt','x', encoding='utf-8')
archivo.writelines(frutas)
archivo.close()


... pero tiene un problema: o modificamos la lista introduciendo "separadores" entre su elementos, o lo que se escribe es una única "palabra" ("PeraPlátanoManzanaNaranjaFresa"). Y ya puestos a ahorrar, si queremos prescindir de la función close() (en archivo.close()) podemos recurrir a with, como, por ejemplo...


frutas = ['Manzana\n', 'Pera\n','Plátano\n']
with open "texto1.txt",'w', encoding='utf-8' as archivo:
	archivo writelines(frutas)


... cuyas sentencias subordinadas (en este caso archivo writelines (frutas)) podemos formular de formas similares a como hicimos en los ejemplos anteriores.

Datos. Archivos de texto

Archivos .txt

Creación y acceso

Hasta este momento hemos usado Python para acceder a archivos estructurados, y aun nos queda trabajo en esa dirección, pero creo necesario tratar, antes de proseguir por ese camino, sobre el trabajo con Python con archivos de contenido no estructurado (creación y acceso), ya que es un conocimiento muy necesario para dar funcionalidad al trabajo con este lenguaje. De no ser así, mucha información que generemos con herramientas basadas en Python se perderán innecesariamente; pero es que, además, dejamos de aprender procedimientos fundamentales para el acceso a datos, objetivo último de esta sección del blog.

Es cierto que aun no contamos con procedimientos de almacenamiento de información no estructurada, aunque ésta es mayoritaria en nuestro campo de trabajo, como fundamental es aprender procedimientos para obtener datos de este tipo de fuentes. Pero antes debemos aprender el manejo básico de este tipo de documentos. De esto va esta entrada y las que siguen.

La forma básica en que se presentan los datos no estrucutrados es en formato .txt y para empezar necesaitamos aprender a crearlos. Para ello usaremos la función open(), que cumple la doble función de crear (de no existir) o abrir (si ya existe) un archivo .txt. Esta función requiere determinados parámetros, viendo condicionado su funcionamiento por cómo se concreten estos.

En este ejemplo archivo = open('texto1.text') creamos o accedemos a un archivo .txt llamado "texto1" que se ubica en la raiz del directorio donde se encuentra el script Python. Otro tanto conseguimos con la instrucción archivo = open('./texto1.text') que remite a la misma ubicación.

Esta segunda expresión nos lleva a plantear la posibilidad de ubicar nuestro archivo (o a buscar su ubicación) en un subdirectorio dependiente de aquel en que se encuentra nuestro script (vg. archivo = open('textos/texto1.text')), así como ubicarlo en cualquier directorio usando una dirección absoluta (vg. archivo =open('C:/Users/NombreUsuario/Escritorio/texto1.txt').

No obstante, tal y como está escrita esta instrucción en cualquiera de sus posibles formulaciones no funciona (devuelve error), ya que precisa de un segundo parámetro que, además, condiciona el funcionamiento de la propia función y de lo que se puede hacer desde el script con el archivo .txt. Me refiero a una serie de cuatro comandos, más otros dos para determinar el tipo de fichero (texto o binario) y uno complementario que comprende los dos modos principales de trabajo: la lectura y la escritura de los archivos.

  • "r" (read) es la opción que utiliza Python por defecto (si no se especifica nada). Permite abrir un fichero para lectura, pero da error si este fichero no existe. Por ello, en el caso en que nos encontramos ahora (creando el archivo txt) no sería adecuado, ya que sabemos de antemano que el fichero existe.
  • "w" (write) abre un fichero para escritura borrando la información que pudiera contener. Si el fichero no existe, lo crea, por lo que es útil para crear nuevos archivos y cuando trabajamos con archivos externos que interesa modificar sin acumular versiones diferentes del mismo.
  • "x" (create) crea un fichero para escribir en él. Si el fichero ya existe devuelve error, por lo que, al contrario que con "r", necesitamos saber que el fichero no existe. También es útil para generar nuevos archivos externos en los que guardar la información generada por el script, siempre y cuando sea necesario crear nuevos ficheros en cada proceso.
  • "a" (append) abre un fichero ya creado para escribir en él situando el cursor al final de su contenido, por lo que no se altera el contenido previo. Esto hace que sea útil cuando queremos añadir la información a la previamente guardada, incluyendo la generada por un script diferente al que creó el archivo original.

Los dos comandos relativos al tipo de contenido (texto o binario) son los siguientes:

  • "b" (binary) abre el fichero en modo binario (valores 0-1 en lugar de texto plano); ficheros como fotografías, archivos ejecutables, ficheros de LO-Writer o MO-Word, por ejemplo.
  • "t" (text-mode) abre el fichero en modo texto plano.

Finalmente el signo "+" abre el archivo en modo lectura y escritura simultáneamente.

El tercer parámetro es opcional, pero la mayoría de las veces muy necesario. Me refiero a encoding (codificación), que indica qué tipo de codificación o juego de caracteres se va a usar en el archivo. La concreción más frecuente de este parámetro es como utf-8

La creación del archivo .txt queda así: archivo = open('textos/texto1.txt','w', encoding='utf-8').

Una vez creado (y abierto) el archivo podremos realizar con él las acciones permitidas por el segundo parámetro que hayamos empleado en la función open(), lo que se concreta como escritura y/o lectura del contenido del archivo, temas estos de los que me ocuparé en próximas entradas.

En todo caso, dado que el archivo está abierto, una vez que hayamos realizado las acciones que precisemos, deberemos cerrarlo, evitando así que quede innecesariamente abierto, consumiendo recursos RAM del sistema. Para ello haremos uso de la función close(), que expresaremos como sigue: archivo.close(), siendo archivo asociada al archivo.

En la actualidad contamos con una expresión que permite acceder a un archivo y cerrarlo de forma automática sin necesidad de hacerlo expresamente. Me refiero a with open().



with open("archivo.txt", "r") as archivo:
    contenido = archivo.read()
    print(contenido)


jueves, 18 de septiembre de 2025

Textos. Python.

Expresiones regulares (I)


Llegados a este punto (1) necesitamos algo más que un programa de acceso y manejo de pdf para avanzar en lo que nos interesa, que es algo más que la "mera" captura del texto (2). En realidad, los recursos ya estaban ahí desde antes que aprendiéramos a acceder al contenido de un documento, pero no habían resultado necesarios. Ahora sí.


Continuando con la búsqueda de soluciones al problema aun no resuelto de mejorar el manejo del texto pdf resultante de la aplicación de la librería PyPDF2 (3), a la vista de la insuficiencia de la segmentación del contenido, parece necesario replantearse el enfoque actual (4) para incluir eso que se ha venido en llamar "operaciones con expresiones regulares" (5).

Las expresiones regulares permite la búsqueda de patrones en textos para encontrar coincidencias, realizar sustituciones o eliminar contenidos. Python hace uso del módulo re, que no necesita ser instalado, pero sí importado o llamado para su uso en los script (import re).

Dada la riqueza (y complejidad) del procedimiento de crear y aplicar los recursos de este módulo, será necesario que le dediquemos tiempo y lo trabajemos de forma específica (6), aunque procuraré acercarme todo lo posible al origen desde el que surgió la necesidad de plantearse este enfoque, esto es, mi intención es que lo que aprendemos sobre re nos sirva para aprender a manejar de forma más eficiente el texto obtenido con PyPDF2

Estoy convencido que de ello surgirán planteamientos de trabajo enriquecedores, que superen los objetivos que ahora nos planteamos (7), pero de momento me contento con...
  • Limpiar y dar la forma deseada al texto pdf capturado.
  • Automatizar el acceso a contenido textual concreto.
  • Generar contenido nuevo a partir y en función del presente en el texto.
De ahí el subtítulo de esta entrada, que es, más que nada, una declaración de intenciones que da continuidad (y enlaza) con la temática precedente en la que se enmarca, pero se adentra ahora en el interesante mundo del manejo de las expresiones regulares como herramientas para el trabajo con textos, que no dejan de ser cadenas complejas más o menos extensas (8).

NOTAS

(1) Nosotros por el camino de automatizar el acceso a pdf, pero bien podría haber sido por otro. En todo caso, aquí nos viene bien porque podemos complementar el acceso al texto con su manipulación, incluyendo (en ella) el acceso a datos concretos, presentes en el texto.
(2) Siendo sinceros, esto nos lo planteamos ahora que ya sabemos cómo acceder al contenido de los pdf. En un momento anterior, nos sentiríamos más que satisfechos con esos logros que ahora parecen insuficientes. Pensándolo bien, esta actual "insatisfacción" no deja de ser buen síntoma.
(3) Además de lo pendiente en cuanto a "limpieza" de contenido, aun nos falta trabajar en el acceso a datos de documentos pdf muy estructurados (en formato tabla). Este tema lo trataremos (en su momento) de forma específica desde la sección [DATOS], ya que a ella corresponde por contenido.
(4) Con independencia del interés que ese conocimiento sobre procedimientos de segmentación tiene para mejorar nuestro manejo del contenido textual; que es mucho y muy importante.
(5) Un contenido muy interesante. Te aconsejo leer con detenimiento [esta página]. En ella se explica en qué consiste el módulo re. Temática nada sencilla, por cierto.
(6) Esto es, no ligada expresa y específicamente la trabajo con PYPDF2
(7) En el fondo de lo que se trata es de acercarnos al análisis de textos como parte del análisis de datos, campo este de trabajo complementario del de la automatización de la composición de textos complejos. Estos dos grandes objetivos siguen siendo nuestras dianas.
(8) Dada la complejidad y extensión de esta temática, muy posiblemente compatibilice su tratamiento con el de otras cuestiones de interés para este blog. Digo esto porque ello puede suponer que el estudio del tema se prolongue en el tiempo.

domingo, 14 de septiembre de 2025

Textos. Python

Librería PyPDF2. Guardar el texto


En sentido estricto esto no es propiamente parte del manejo de PyPDF2, pero sí parte necesaria del proceso, ya que de poco sirve obtener el contenido de un documento si no podemos disponer de él para seguir avanzando en nuestro proyecto. Es absurdo, y más teniendo a mano soluciones tan sencillas pero tan eficientes como la que explico en esta entrada.


Sea sobre el texto tal y como lo obtenemos mediante PyPDF2, sea tras su tratamiento, por ejemplo, mediante algún procedimiento de segmentación y recomposición como los explicados [en esta entrada], lo cierto es que necesitamos rescatar para un trabajo posterior el texto obtenido. Las formas son varias, como lo son los soportes, pero aquí vamos a exponer la más simple de todas: el archivo del contenido como documento txt.

Para ello sólo necesitamos añadir al código de nuestros script dos instrucciones que forman parte de una estructura wiht (1):

with open("pdf/textos/contenido_pdf1.txt", "w", encoding="utf-8") as archivo:
    archivo.write(texto)

Si te fijas, dentro del paréntesis que sigue a with open() tenemos...

  • En primer lugar y como primer parámetro, un string con la dirección donde guardar el archivo y el nombre (y extensión del propio archivo ("pdf/textos/contenido_pdf1.txt"). 
  • El segundo parámetro indica el tipo de acción a ejecutar, en esta caso w de writing (escribir)
  • Y el tercero y final el tipo de codificación (encoding) que vamos a emplear en este archivo txt; en este caso  ("utf-8") (2)
Completa esta instrucción la orden que ejecuta lo que previamente se ha establecido (archivo.write(texto)) mediante la función writer() aplicada al contenido  de la variable (texto) a la que hemos asignado el texto obtenido mediante PyPDF2 en la fase previa de nuestro script (3).

NOTAS

(1) Ya tuvimos ocasión de trabajar con esta estructura en el script de captura de imágenes [Ver entrada] en el que nos permitía ejecutar un procedimiento similar, pero relativo a la copia de las imágenes:

 with open(f'pdf/imgs/{str(n_pag)}__{image_file_object.name}', 'wb') as img:    
            img.write(image_file_object.data)

(2) Si prescindimos de esta codificación es muy probable que se produzca error a poco que el texto capturado contenga caracteres que se "salen" de los registros más básicos.
(3) Este código es tan simple que podemos utilizarlo sin mayor dificultad y con mínimos cambios para trabajar con otros script basados en PyPDF2 dirigidos a la obtención del texto del pdf.