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

jueves, 9 de octubre de 2025

Demanda. Propuesta.


Demanda de evaluación (VI)




Traslado de la demanda al SEO

Una vez que el Equipo Educativo ha elaborado su demanda, esta, de un modo u otro, pasa al SEO, que valora la información y se plantea qué hacer (y cómo), pero esas son cuestiones que pertenecen a otros procesos. Por lo que a éste se refiere, tras lo trabajado en las entradas anteriores y en lo que son los términos estrictos de esta sección, sólo queda resolver la recepción del documento por parte del SEO y el modo en que éste va a trabajar con dicho documento.

Al respecto, aunque son varias las posibilidades, la que me interesa plantear ahora es aquella en la que, quien corresponda, nos entrega un documento pdf (formato digital)(1). Nuestro objetivo como SEO es acceder al contenido, especialmente a la parte que contiene la información crítica (DA y dificultades EA, página 2), ya que sobre ella vamos a tomar decisiones y a montar un procedimiento de actuación. Nuestro interés es automatizar este acceso o, cuanto menos, ayudarnos de Python para acceder a esa información. Para este fin utilizaremos la biblioteca PyPDF2. Este sería el script básico...

from PyPDF2 import PdfReader

lector = PdfReader("solicitud.pdf")

pagina = lector.pages[1]

texto = pagina.extract_text()

... con el que podríamos dar por concluida esta tercera fase del proceso simplemente con este script, ya que cubre satisfactoriamente (aunque por la mínima) con el objetivo que enunciamos antes: nos permite acceder al documento (lector = PdfReader("solicitud.pdf")), concretamente a su página 2 (pagina = lector.pages[1]), de la cual extraemos su contenido (texto = pagina.extract_text()). Como puedes ver, mucho con muy poco (2).

Y ciertamente, en este punto podemos dar por finalizado el enfoque de trabajo que hemos mantenido en este proyecto hasta este momento (Python como lenguaje para construir script con los que abordar procedimientos), a la vez que iniciado (estamos aquí en una frontera borrosa) otro enfoque, más instrumental, en el que me planteo usar Python como herramienta para manejar el material (en este caso, el contenido de la segunda página de un documento pdf) que hemos recibido.

Como este planteamiento puede resultar un tanto confuso, aunque no es la primera vez que lo planteo, me parece una buena ocasión para ponerlo en práctica, lo que requiere que nos resituemos como SEO y nos planteemos con qué objetivos vamos a trabajar, ya que son estos los que nos van a guiar en el proceso.

En primer lugar, la finalidad última de nuestro trabajo se concreta como uso de la información sobre DA y dificultades EA para plantear actuaciones, la más inmediata es construir un guion de contenidos para una entrevista con el profesorado (tutor/a). 

Ahora bien, para esto necesitamos hacer algo más (que se aparta de lo que ahora nos planteamos), que es investigar en el expediente escolar del alumno, y no es estrictamente necesario que lo que sí podemos considerar parte de este nuestro actual procedimiento sea resuelto mediante Python: perfectamente podemos copiar el contenido de la salida que nos ofrece el script anterior si el añadimos un print(texto), y pegarlo en un documento (txt o Writer, por ejemplo) y ya podríamos trabajar con el contenido que nos interesa. Esta podría ser tu opción.

De hecho esto ya constituye una forma, la más simple que no la más útil (3), de aplicar ese enfoque directamente instrumental del uso de Python del que te estoy hablando. Lo que te propongo en esta entrada es desarrollarlo de forma más extensa y coherente con un planteamiento orientado a la automatización de los procedimientos de trabajo.

La cuestión está en que, desde este enfoque, no existe un script que entregar como base para tu uso (4); es el script lo que tienes que ir construyendo hasta obtener el resultado que te satisfaga. Esto implica formularlo y reformularlo conforme avanzas en un proceso que vas construyendo en parte en función de tu objetivo-guía, en parte en función de los resultados que vas obteniendo.

En este caso yo me planteo obtener por separado los datos DA observadas y dificultades EA observadas y almacenar ambos en sendos archivos txt para su posterior uso. Hasta aquí llegará mi trabajo con el contenido extraído de l documento PDF.

Esto implica desarrollar dos veces el siguiente proceso:

  • Extraer del documento las líneas correspondientes con la categoría 
  • Pasarlas a una lista específica 
  • Copiarlas en un archivo txt específico
Además, si se da el caso (que se da) será necesario modificar alguno de los elementos de estas listas (5)

Este planteamiento es el que se recoge en el código que te proporcionó en el archivo contenido en el zip que puedes descargar desde [este enlace] y que contiene todos los script del proyecto y todos los documentos auxiliares y aquellos que sirven a modo de ejemplo (6)

NOTAS

(1) En realidad no debería ser infrecuente recibir el conjunto de documentación generada, incluyendo el archivo datos.txt. No obstante ahora vamos a trabajar en base a una realidad tampoco inusual que si no es en este contexto puede perfectamente darse en otros. No es infrecuente tener que trabajar con información aportada en PDF e incluso, aun peor, con documentos en papel, los cuales deberemos escanear si deseamos acceder a su contenido. En ambas situaciones podremos hacer uso de la biblioteca PyPDF2 (u otra similar) como herramienta para automatizar (parcialmente) el acceso al contenido textual de esa documentación.
(2) Se debe entender que estamos usando Python (concretamente PyPDF2) como una herramienta, así que nuestro objetivo con este script no es crear una aplicación (por sencilla que sea) sino obtener un resultado inmediato usando el lenguaje como herramienta de trabajo que, en este caso, sustituye al copia-pega. Este enfoque de trabajo facilita interesantes desarrollos, como podremos ver a continuación en esta misma entrada.
(3) No es precisamente ésta la mejor solución, ya que el resultado del print()-copia-pega nos proporciona un texto plagado de trabajo, pero como ejemplo de lo que quiero decir me sirve perfectamente.
(4) Ese ha sido el planteamiento de los script anteriores dentro de este proyecto. En realidad sí podríamos crear un "borrador" de script con todas (o casi) las instrucciones comentadas para que tú las fueras activando conforme las fueras necesitando. Y puede ser una solución interesante cuando es de esperar que podamos repetir varias veces un mismo proceso, pero en la mayoría de los casos es más bien un esfuerzo excesivo para un resultado incierto. En cualquier caso lo que sí parece interesante es ir comentando el script conforme lo vamos construyendo para saber la motivación y objetivo de cada uno de los pasos que vamos dando: a partir de esta guía pueden surgir procedimientos de interés que podemos retomar como proyectos de script/app en otro momento.
(5) Cuando se trabaja con texto extraído de un PDF no hay garantías de correcta codificación, por lo que es posible que tengas que trabajar con el texto resultante. Este es el caso respecto a las tildes.
(6) Obviamente se trata de un supuesto, no de datos reales.

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.

sábado, 13 de septiembre de 2025

Textos. Python.

Biblioteca PyPDF2. Segmentar el contenido (otras unidades)


Aunque dividir el contenido por páginas es de interés, mayor cuanto de más páginas conste el documento, no es la única segmentación posible ni la única de la que podemos obtener beneficios a la hora de automatizar el acceso a un documento pdf. En esta entrada veremos otras segmentaciones posibles.


La primera que se me ocurre resulta de la aplicación de la función split() la cual, [como sabes] (1) sirve para separar los elementos de una cadena en función de un carácter usado como criterio de segmentación.

La aplicación de split() para crear una lista la realizamos sobre el contenido de la variable resultante de la captura del contenido textual del (o de los) pdf, por lo que mantenemos el código anterior y simplemente implementamos el nuevo (2).

En función del objetivo que busquemos con la segmentación, el cual a su vez puede estar determinado por la extensión (y otras características) del texto, deberemos utilizar el carácter divisor que más se ajuste a nuestro objetivo. 

Aunque el más simple consiste en obtener una lista con las palabras del texto usando simplemente la instrucción split() (3), posiblemente uno de los de mayor utilidad en caso de texto extensos y complejos de forma sea el salto de línea (\n), para el que, ya sabemos, contamos con la función específica splitlines().

Lo cierto es que, combinando este criterio de separación por saltos de línea con la función de recomposición de la unidad del texto mediante join() (4), podemos intentar resolver una de las "limitaciones" que observamos cuando trabajamos con pdf multi-columna.

from PyPDF2 import PdfReader

#Acceso al documento
lector = PdfReader("pdf/EvalTDAH.pdf")
pag = len(lector.pages)
texto = ""

for pg in range(pag):
    pagina = lector.pages[pg]
    texto += pagina.extract_text()

#Variables para cargar info
pag = len(lector.pages)
meta = lector.metadata 
num_pal = len(texto)

#Imprimir en consola
print("DATOS DEL DOCUMENTO --------------------------")
print("Número de páginas",pag)
print("Número de palabras",num_pal)
print("METADATOS DEL DOCUMENTO --------------------")
print("Autor",meta.author)
print("Título",meta.title)
print("TEXTO completo -----------------------------------")
print(texto)

#Segmentación del contenido en función de las líneas

listalin = []
listalin = texto.splitlines()

#Recomposición de la unidad del texto
textofin = ""
textofin = " ".join(listalin)

#Mostrar el nuevo texto por pantalla
print(textofin)

Muy cierto que pasamos de un problema a otro, pero no pretendo aquí más que mostrar lo que resulta de combinar split() con join() en el manejo de textos amplios y complejos. Evidentemente se puede mejorar mucho el resultado, pero necesitamos trabajar más el script, así que, por el momento voy a considerarlo suficiente.


NOTAS

(1) También sabes tanto OOo Basic como Python cuentan con esta función para la segmentación de cadenas, que además funciona del mismo modo en ambos lenguajes. Para OOo Basic puedes consultar [esta entrada].
(2) Al contrario de lo que hicimos cuando segmentamos el contenido en función de las páginas del pdf [ver entrada anterior]
(3) La utilidad inmediata de este procedimiento es conocer el número de palabras del texto mediante la función len(), pero no el la única; hay todo un mundo de opciones detrás de esta segmentación.
(4) Vale para join() el enlace asociado a 1 en el cuerpo de la entrada y lo dicho en esa misma nota para split()

viernes, 12 de septiembre de 2025

Textos. Python.

Biblioteca PyPDF2. Segmentar el contenido (páginas)


Resuelto (en parte) el [acceso a las imágenes] de un pdf (1), nos queda aun mucho por delante para mejorar los procedimientos de modo que nos sea realmente de utilidad. Y se me ocurre que segmentar el contenido, aunque no sea la solución, sí nos acerque a ella.

Segmentar el contenido nos puede ayudar de muchas maneras, siendo la primera de ellas facilitar el acceso a segmentos del texto desde el script, cosa que resulta complicada cuando el contenido textual es amplio. Posiblemente no sea la única ventaja, así que merece la pena explorar esta opción.

Para ello debemos tener en cuenta las propias divisiones de un texto en páginas, párrafos, líneas y palabras, algunas de las cuales nos pueden interesar como criterio de segmentación. También las funciones de que dispone Python para trabajar con cadenas y las propias colecciones de datos de este lenguaje.

Tomando las listas como colección básica y la página como referencia para la división, una primera segmentación es capturar de forma diferenciada y en una lista, el contenido de cada una de las páginas de un texto. En teoría esto nos podría servir para trabajar de forma diferenciada con cada una de las páginas, incluyendo el acceso selectivo en función de objetivos (2).

A modo de ejemplo, tomando como referencia el mismo documento multi-página que nos sirvió para aprender a acceder a un documento de estas características (3), te planteo ahora el script que nos permite acceder a cada página por separado.

from PyPDF2 import PdfReader

#Acceso al documento
leer_doc = PdfReader("pdf/Curriculo.pdf")
pag = len(leer_doc.pages)
pag_guardar= []

# Recorrer las páginas del documento asignándolas a los componentes de la lista pag_guardar
for i in range(pag):
    pagina = leer_doc.pages[i]
    pag_guardar.append(pagina.extract_text())

#Imprimir en consola

print("INICIO DEL TEXTO -----------------------\n")
for i in range(pag):
    print(pag_guardar[i] + "\n\n FÍN Página " + str(i+1) + "  -------------------------------\n\n")
print("\nFIN DEL TEXTO----------------------")

Puedes comparar este script con el que nos sirvió para acceder a las múltiples páginas del documento y cargarlas sobre la variable texto (4) y compararlo con el actual:

  • Observarás que ahora declaramos una lista para contener las diferentes páginas (pag_guardar= []) y no una variable.
  • Y lo que es más importante, ahora cargamos cada una de las páginas sobre cada uno de los elementos de la lista mediante la función append() (pag_guardar.append(pagina.extract_text())), siendo ésta la instrucción que sustituye a la que nos sirvió en su momento (texto += pagina.extract_text())
El resultado es un script realmente sencillo que después podemos "adornar" de diferentes maneras, incluyendo el que aquí presentamos en la parte final del script, que consiste en recorrer la lista mediante un bucle, mostrando página a página (5)



NOTAS

(1) Empecé por esta cuestión por ser evidente la carencia y a la vez por ser sencilla la solución. Desgraciadamente comprobamos que no es definitiva; pero sí prometedora (cuando funciona) para automatizar procedimientos de creación de soportes que emplean imágenes, como, por ejemplo, la creación de presentaciones.
(2) Aquí, por el momento, nos vamos a contentar con hacer posible la segmentación del documento en páginas, siguiendo la misma división que presenta el texto original.
(3) Remito a [la entrada] desde la que puedes acceder al documento
(4) Recuerda la instrucción que empleamos en ese script: texto += pagina.extract_text()
(5) No se trata ahora de plantear las múltiples formas con las que podemos trabajar con el contenido de cada página, pero, además del presentado se me ocurre, por ejemplo, el acceso selectivo a determinadas páginas.

miércoles, 10 de septiembre de 2025

Textos. Python.

Biblioteca PyPDF2. Imágenes.


Hasta este momento hemos visto como usar PyPDF2 para acceder al contenido de documentos pdf; también conocemos algunas de sus limitaciones, pero aun nos queda mucho por aprender respecto al acceso y el manejo de los datos recuperados. Es más, posiblemente lo realmente importante, lo que nos puede aportar soluciones de interés para nuestro trabajo, está aun por tratar. La idea es que empecemos a hacerlo en esta entrada.


Hasta ahora hemos traslado la referencia y acceso al texto obtenido mediante PyPDF2 a una variable y nos hemos limitado a mostrar ese contenido en pantalla, pero es difícil de sostener que ese esa un modo razonable de trabajar; es más, posiblemente esa sea una forma contraproducente y limitante de trabajar, responsable además de algunas de las limitaciones que hemos observado en la [entrada anterior]. Nos interesa ahora plantearnos formas más razonables de manejar y tratar el volumen de información al que nos permite acceder PyPDF2, aunque para ello debamos establecer, además, qué objetivos y con qué restricciones nos planteamos el uso de esta biblioteca.

Digo esto porque, dependiendo del objetivo, una teórica limitación puede serlo o no (o no tanto); y dependiendo de las restricciones (por ejemplo, qué tipo de documento pdf usamos como fuente de datos) nos deberemos plantear un procedimiento de trabajo u otro. Si lo que queremos es acceder a varios documentos y unificar todos los datos que contienen en uno único, no es lo mismo que si lo que nos interesa es acceder a un contenido o tipo de contenido en concreto. Si trabajamos con textos más o menos extensos basados en párrafos no es lo mismo que si trabajamos con textos muy estructurados en forma de tablas. 

Podría seguir planteando opciones y condiciones, pero lo que me interesa es transmitir que nuestro enfoque de trabajo puede ser diferentes según condiciones y objetivos, y que esa diferencia incide en qué consideremos ventaja o limitación, y, en consecuencia, también en cómo abordamos la solución de la segunda.

Dicho esto, por entrar en materia, consideremos una de las limitaciones que vimos en su momento, y uno de los aspectos en los que lo es: el acceso a las imágenes contenidas en un pdf, consideradas éstas como tales (1).

Dentro del interés por "apropiarnos" del contenido de un pdf puede estar hacerlo de las imágenes que contiene, sean estas gráficos de esquemas, fotografías o dibujos o gráficos estadísticos. Sabemos hasta ahora que PyPDF2 no permite acceder a esas imágenes; y sabemos también que, si son pocas, podemos capturarlas directamente mediante la funcionalidad Capturar pantalla, pero si son muchas o son muchos los documentos, automatizar la captura con Python sin duda es una buena solución (2).

Empecemos por la limitación y por cómo solucionarla: hasta ahora sabemos que PyPDF2 no permite extraer imágenes y hemos comprobado esta limitación en la práctica [ver entrada]; pero en realidad esta biblioteca cuenta con recursos para extraer las imágenes de un pdf. Veremos cómo hacerlo sobre un documento que versa sobre el arte Románico (3).

Suponemos la existencia de un directorio que contiene nuestro script, dentro del cual hemos creado un subdirectorio que contiene los archivos pdf (pdf), incluyendo en este caso el que nos interesa (ArteRomanico.pdf). Dentro de este subdirectorio creamos otro para guardar las imágenes (imgs).

El script podría ser el siguiente:

import PyPDF2

archivo = input("Nombre del archivo (sin la extensión): ")
lector = PyPDF2.PdfReader(f'pdf/{archivo}.pdf')

paginas = len(lector.pages)

for n_pag in range(paginas):
    pag = lector.pages[n_pag]
    for image_file_object in pag.images:
        with open(f'pdf/imgs/{str(n_pag)}__{image_file_object.name}', 'wb') as img:    
            img.write(image_file_object.data)

print ("Imágenes guardadas")

Después de cargar la biblioteca (import PyPDF2) (4) se desarrolla la primera fase del script, que consiste en los siguiente: 
  • Creamos un procedimiento interactivo basado en la función input() para  que el usuario aporte el nombre del archivo (que, como sabes, debe estar ubicado en el directorio pfd/) (archivo = input("Nombre del archivo (sin la extensión): ")). 
  • Sobre la variable lector construimos la instrucción basada en la función PdfReader(), la cual nos permite acceder al documento que contiene las imágenes (en nuestro caso ver 3) (lector = PyPDF2.PdfReader(f'pdf/{archivo}.pdf')).
  • Finalizamos asignando a la variable paginas el número de páginas del documento mediante la función len() (paginas = len(lector.pages)) (5)
La segunda fase del script se concreta como una estructura compleja de bucle que anida otro bucle, que a su vez contiene una instrucción with
  • El primer bucle (for n_pag in range(paginas):) recorre las páginas del documento y captura cada una de ellas en la variable pag (pag = lector.pages[n_pag])
  • El segundo bucle (anidado en el anterior) (for image_file_object in pag.images:) recorre la colección de imágenes que contiene cada una de las páginas del documento y desarrolla la instrucción with (6).
  • Ese instrucción desarrolla la acción de acceder al directorio y a las imágenes y las copia, y la finaliza, evitando que quede pendiente de ser finalizada (ver nota 6).
  • Finalmente añadimos una instrucción print() para informar del éxito del procedimiento (print ("Imágenes guardadas")) (7)
De esta forma tan sencilla hemos creado un "capturador" de imágenes que permite solucionar una de las limitaciones de PyPDF2. No siempre funciona bien (8), pero cuando lo hace resulta muy útil.


NOTAS

(1) Esto es, al margen de aquellas que contienen texto "fotografiado". Este segundo tipo requiere un tratamiento específico que no es posible dar con PyPDF2, como tendremos ocasión de comprobar.
(2) Observa que aquí abordamos una limitación y cómo superarla en el contexto del logro de un objetivo concreto (obtener imágenes de un documento pdf), planteándonos un manejo realista de la documentación resultante y con una doble posible condición: un pdf con muchas imágenes o una colección de pdf de los que nos interesa extraer las imágenes.
(3) Contexto: ArteRománico.pdf contiene una colección amplia de imágenes sobre el arte románico que, con los debidos permisos de uso, nos serán de utilidad para proporcionar material gráfico a un grupo de alumnos interesados por el tema. Para ello vamos a crear un script basado en PyPDF. Para lograr este objetivo me basaré en la información que aporta Programación para el mundo en You Tube.
(4) Recuerda que también puedes usar la fórmula from PyPDF2 import PdfReader, lo que te permite simplificar la estructura de la instrucción asociada a lector (lector = PyPDF2.PdfReader(f'pdf/{archivo}.pdf'))
(5) La función len() actúa sobre el atributo .pages del objeto (documento) asignado a la variable lector (en(lector.pages)), dado que ese atributo devuelve la colección formada por las páginas del documento.
(6) Según [los entendidos] esta permite ejecutar una acción que tiene un inicio (en nuestro caso el  fragmento de código que contiene la instrucción de acceso al subdirectorio imgs/ y a cada una de las imágenes de la página (open(f'pdf/imgs/{str(n_pag)}__{image_file_object.name}', 'wb') as img) y un final (en nuestro caso la "escritura de la imagen obtenida ( img.write(image_file_object.data))
(7) Aquí correspondería manejar la excepción mediante el procedimiento pertinente, pero dada la simplicidad del proyecto considero suficiente la información que aporta el IDE.
(8) De momento no tengo respuesta ni alternativa a esta limitación, la cual se da con más frecuencia de la deseada: no es infrecuente que PyPDF2 falle en el acceso a imágenes, bien por no detectarlas, por fracasar en el acceso a las mismas o por devolverlas como su negativo. 

martes, 9 de septiembre de 2025

DATOS. Archivos PDF

Biblioteca PyPDF2 (V)

Limitaciones

Hasta ahora hemos visto diferentes formas de trabajar con documentos .pdf para acceder a su contenido, pero nos hemos limitado a lo básico y positivo. Aunque lo que conseguimos no es poco (1), la verdad es que nos hemos saltado algunas limitaciones y no podemos decir que la información obtenida resulte de fácil manejo. Dedicaremos esta entrada a exponer algunas limitaciones que presenta PyPDF2.

La primera y más radical de ellas es que PyPDF2 no nos da acceso al contenido gráfico de los .pdf, incluyendo imágenes que contienen texto. Esto es así porqueesta lisbrería no es un OCR, pero esta limitación, que puede tener poca importancia cuando las imágenes no contienen texto, es importante cuando trabajamos con documentos .pdf que contienen texto guardado como imagen. Y este contenido es más frecuente de lo que creemos, especialmente cuando trabajamos con documentación antigua digitalizada. Por ello esta carencia supone un hándicap importante para la automatización del acceso a ese tipo de documentos (2).

Para probar lo dicho es suficiente con observar qué ha sucedido con la imagen de fondo del documento multi-página con el que trabajamos en esta entrada, que simplemente ha desaparecido (3). Pero lo que aquí es una ventaja, en este otro caso es un serio inconveniente.

Aunque el script ahora es lo de menos (lo que importa es el documento al que se accede), te remito al expuesto en esta entrada, aunque debes cambiar la línea de acceso al .pdf por esta otra (lector= PdfReader("pdf/DocImg.pdf")) para obtener el que ahora me interesa y cuyo contenido te muestro a continuación.

Como puede ver, no se trata de un documento antiguo ni difícil de trabajar con OCR, pero sí cumple el criterio principal: aunque no lo parezca, es una captura de pantalla (img) pegada en un documento Writer, convertido posteriormente en documento .pdf. El procedimiento no se diferencia mucho del practicado en otro caso (4), pero el resultado es radicalmente distinto: en el primero obtenemos el contenido del documento, pero aplicado ese mismo script a este (DocImg.pdf) (5) el resultado es nada.

Esta misma limitación la observamos respecto a los elementos gráficos de este otro pdf (EvalTDAH.dpf) (6). Los gráficos estadísticos que contiene han desaparecido, aunque PyPDF2 nos permite rescatar alguna información asociada a ellos. También nos permite acceder a la información de las tablas del documento (7), pero se presentan de forma poco accesible. Con todo, lo que posiblemente sea aun más decepcionante es la forma en que aparece el texto en pantalla: al estar expresado en formato doble columna obtenemos líneas acortadas aunque correctas en cuanto a su sucesión; esto hace innecesariamente más extenso el texto y no facilita su lectura y el trabajo con su contenido (8).

Aquí nos encontramos, pues, con limitaciones/dificultades de uso de PyPDF2 que afectan a la presentación pero también a la recuperación y el manejo del texto. Incluso también, como resultado de lo segundo, al acceso a información concreta, siendo como es este el principal objetivo por el que usamos esta biblioteca.

Vistas y demostradas limitaciones de importancia para el uso que podemos hacer de PyPDF2, me propongo en próximas entradas presentar soluciones, incluyendo las que requieran recursos complementarios a esta biblioteca; pero sobre todo avanzando en un uso más potente de lo que Python nos puede aportar.

Documentos.

Nota:

1 Y da para unas cuantas cosas, algunas de las cuales veremos en otro momento.
2 Estoy pensando por ejemplo en el trabajo con informes médicos que, por su formato, resultan complicados de escanear como texto, por lo que se suele recurrir al escaneo como imagen.
3 Cosa que en este caso nos viene bien, ya que lejos de aportar algo sólo podría servir para dificultar la lectura automatizada del documento.
4 Salvo el tratamiento como img, el resto del procedimiento es exactamente el mismo que seguí para crear VueltaCavernas.pdf. Ten en cuenta que esta misma limitación la tenemos para acceder a pdf que contienen controles de formulario: en ellos podemos acceder a los elementos textuales de las tablas, pero no al contenido escrito en los controles. Esto supone una seria limitación, de la cual no hablamos en esta entrada por corresponder a desarrollos o aplicaciones concretas de PyPDF2 como herramienta de automatización a documentos
5 Enlace para acceso en Documentos
6 Por tratarse ahora de un documento multi-página deberemos utilizar el script presentado en esta entrada, de nuevo modificando la instrucción que especifica el archivo al que accedemos (lector = PdfReader("pdf/EvalTDAH.pdf")). También es este caso puedes acceder al .pdf desde Documentos.
7 Ver, por ejemplo, el contenido de la página 6 del documento en la versión pdf del mismo y en el texto que el script devuelve en pantalla.
8 Mucho más aun el acceso contenido específico que nos pudiera interesar. Este en, en realidad, la segunda de las limitaciones o dificultades de las que hablo en la intro de esta entrada.

DATOS. Archivos PDF

Biblioteca PyPDF2 (IV)

Acceder a múltiples archivos

Tarea complementaria de la anterior (acceder a múltiples páginas) es la que me propongo ahora: acceder de forma automatizada a varios archivos .pdf. Se trata también de una práctica que realizaremos con cierta frecuencia, así que interesa aprender cómo.

Realmente es menos relevante para este objetivo que el archivos tenga una página o varias, pero sí es seguro que automatizar el acceso a varios documento nos va a ser de mucha ayuda, sea para acceder al texto o para la búsqueda selectiva de información concreta en múltiples documentos de igual estructura.

Para llevar a cabo esta tarea, además de PyPDF2 necesitamos utilizar también la biblioteca os, gracias a la cual podremos automatizar el acceso a todos los documentos ubicados en una ruta (directorio) determinada. Lo que resta es ya tarea que corresponde a PyPFD2, como veremos a continuación.

Para esta práctica he creado un directorio auxiliar llamado coleccion1/, que contiene cinco documentos .pdf simples (de una página) que son la repetición de uno con el que ya trabajamos en esta entrada (1). Este es el código del script.



import os
from PyPDF2 import PdfReader

#Accedo a directorio pdf/coleccion1
os.chdir('pdf/coleccion1')

#Creo variables para acceso al contenido de los documentos
texto = ''
separador = "\n\nSiguiente documento--------------\n\n"

#Listo los componentes del directorio
lista_archivos = os.listdir('.')

#... y los recorro accediendo al contenido además de contarlos para identificar su número
i = 0
for lista in lista_archivos:
	lector = PdfReader(lista)
	pagina = lector.pages[0]
	texto += pagina.extract_text() + separador
	print(lista)

#Imprimo en pantalla el número de registros...
print("Número total de archivos " + str(i))

# ... y su contenido
print("\nINICIO DEL DOCUMENTO PDF ------------------------\n")
print(texto)
print("\nFIN DEL TEXTO DEL DOCUMENTO PDF ----------------")


Gracias a la biblioteca os y siguiendo el procedimiento ya explicado en esta entrada puedo acceder a los archivos del directorio que me interesa (2) y mediante el bucle for lista in lista_archivos: los recorro accediendo a su contenido gracias a lector = PdfReader(lista) y al procedimiento de acceso a la página pagina = lector.pages[0]. El procedimiento finalizar asignando acumulativamente el contenido de cada página a la variable texto += pagina.extract_text() + separador, que después imprimimos por pantalla (print(texto)), aunque podríamos tratarla de otro modo.

Si en lugar de trabajar con documentos .pdf de una página trabajáramos con otros de múltiples páginas, pero sólo quisiéramos acceder al contenido de una de ellas, usaríamos el mismo script modificando la referencia al directorio (vg. os.chdir('pdf/coleccion2')) y el índice de la página en la instrucción (pagina = lector.pages[4]) (3).

Finalmente puede que nos interese acceder a todo el contenido de unos archivos multi-página. Para ello partiremos del script anterior, pero modificándolo de forma sustancial...



import os
from PyPDF2 import PdfReader

#Accedo a directorio pdf/coleccion2
os.chdir('pdf/coleccion2')

#Creo variables para acceso al contenido de los documentos
pag = 0
texto = ''
separador = "\n\nSiguiente documento--------------\n\n"

#Listo los componentes del directorio...
lista_archivos = os.listdir('.')

#... y los muestro en pantalla
i = 0
print("Listado de los archivos del directorio")
for lista in lista_archivos:
	print(lista)
	i = i +1
print("Número total de archivos " + str(i))

# Vuelvo a recorrer la lista de archivos, copio su contenido...
for lista in lista_archivos:
	lector = PdfReader(lista)
	pag = len(lector.pages)
	for pg in range(pag):
		pagina = lector.pages[pg]
		texto += pagina.extract_text()

# y lo muestro en pantalla
print("\nINICIO DEL DOCUMENTO PDF ------------------------\n")
print(texto)
print("\nFIN DEL TEXTO DEL DOCUMENTO PDF ----------------")


... primero, para hacerlo más claro separo el recorrido del directorio y la escritura del título de los documentos y su conteo a un bucle específico...



i = 0
print("Listado de los archivos del directorio")
for lista in lista_archivos:
	print(lista)
	i = i +1
print("Número total de archivos " + str(i))


... y segundo, recorro todas las páginas de cada uno de los archivos y las paso a la variable texto mediante un segundo bucle que recorre de nuevo los archivos y anida un segundo bucle que recorre las páginas de los documentos (4).



for lista in lista_archivos: 		# Primer bucle: recorre los archivos del directorio
	lector = PdfReader(lista)
	pag = len(lector.pages)
	for pg in range(pag):			# Segundo bucle: recorre todas las páginas de cada uno de los pdf.
		pagina = lector.pages[pg]
		texto += pagina.extract_text()


Notas:

1 De este modo me ahorro subir los archivos, aunque tú tendrás que replicar la carpeta y su contenido. Recuerda crear dentro de coleccion1 de VueltaCavernas0.pdf a VueltaCavernas4.pdf.
2En este script mostrarlos en pantalla y contarlos es sólo un modo de conocer que el procedimiento funciona correctamente.
3 Para este ejemplo he creado un segundo directorio (coleccion2) que contiene repetido el archivo multi-página usado en esta entrada. Para crear el script que acceda a una de sus páginas (la 5, en el ejemplo indicado en cuerpo de la entrada) sólo tienes que realizar sobre el explicado los cambios que se proponen.
4 Para esta tercera opción puedes usar el mismo directorio que en la segunda versión del primer script (ver nota 3)

Datos. Archivos PDF

Biblioteca PyPDF2 (III)

Archivo multi-página

Siguiendo con lo bueno y fácil de resolver, trataremos en esta entrada sobre el acceso a los documento multi-página, que son los más.

Aunque acceder a una página concreta de un documento o a la única página de un documento simple no presenta mayor dificultad según vimos en la entrada anterior, hacerlo a todo el contenido de un documento compuesto por múltiples páginas será seguramente de mayor utilidad, dado que son los más; y ejemplos confirmatorios de lo anterior hay muchos (1).

Por suerte acceder a todo ese texto es uno de los problemas de fácil solución mediante PyPDF2. Y para comprobarlo vamos a trabajar con un documento multi-página no especialmente complejo, aunque sí lo suficiente como para que nos permita observar alguna de las utilidades y también de las limitaciones de PyPDF2 (2).



from PyPDF2 import PdfReader
leer_doc = PdfReader("pdf/Curriculo.pdf")
pag = len(leer_doc.pages)
for pg in range(pag):
	pagina = leer_doc.pages[pg]
	texto += pagina.extract_text()
print("INICIO DEL TEXTO -----------------------\n")
print(texto)
print("\nFIN DEL TEXTO----------------------")


Me interesa que compares esta línea (pagina = lector.pages[0]) con esta otra (pag = len(leer_doc.pages)). La primera nos permite acceder directamente a una página determinada (3) y la segunda nos da acceso a todas (gracias a la función len(), pero también al atributo .pages de la variable objeto leer_doc), pero necesitamos crear un bucle para recorrerlas...



for pg in range(pag):
	pagina = leer_doc.pages[pg]
    texto += pagina.extract_text()
    

... acumulando el resultado del recorrido (pagina = leer_doc.pages[pg]) en la variable texto="" previamente declarada mediante la instrucción texto += pagina.extract_text() (4)

Documento. Desde este enlace] puedes acceder y descargar el documento pdf multi-página que nos sirve de recurso en esta entrada (5).

Nota:

1 Artículos de revistas especializadas o de informes publicados on-line, informe de servicios externos... son algunos de esos documentos multi-página cuyo contenido nos puede interesar. De hecho ya quisiéramos que el único problema fuera acceder a todas las páginas del documento.
2 Este documento ha sido publicado por la Consejería de educación y presenta más complejidad que estar formado por varias páginas, pero de momento nos centraremos únicamente en esa característica. Puedes acceder al pdf desde el enlace que te proporciona en Documento.
3 En este caso la primera página, pero podría ser cualquier otra. Este procedimiento nos sirve tanto para trabajar con textos de una única página como con textos de varias páginas, pero de las que sólo nos interesa acceder a una de ellas. Recuerda que el índice inicia en 0, de ahí [0] para referirse a la primera página del documento.
4 El resultado no es todo lo satisfactorio que desearíamos, incluyendo el efecto no deseado de la repetición de la cabecera y del pie de página, pero se cumple el objetivo principal del script: capturar en la variable texto todo el contenido del documento.
5 Recuerda copiar este documento dentro de la carpeta pdf/ o bien modificar la línea del script

lunes, 8 de septiembre de 2025

DATOS. Archivos PDF

Biblioteca PyPDF2 (II)

Archivo simple

No debería ser un tema que nos interesara, pero la realidad es que sí: en lo que al acceso al contenido se refiere, con PyPDF2 obtendremos resultados diferentes en función de cómo esté construido el archivo con el que trabajemos, ya que un .pdf puede ser muy simple o muy complejo. Por simple vamos a entender aquel que contiene únicamente un texto sencillo, con independencia de su extensión (1) y por complejo aquel que contiene diversos elementos, incluyendo gráficos y configuraciones del texto en columnas (2).

En esta entrada trataremos el caso más básico, aunque posiblemente no el más frecuente: un documento de texto simple y de página única (3). También podemos aplicar esta calificación al trabajo con un .pdf complejo del que únicamente queramos obtener una página de texto simple (4).

Sobre esta base y sin más pretensión que la didáctica, voy a finalizar el script iniciado en la entrada anterior



from PyPDF2 import PdfReader
lector = PdfReader("vueltaCavernas.pdf")
pagina = lector.pages[0]
texto = pagina.extract_text()
print(texto)


Es muy sencillo acceder al contenido de un .pdf sencillo: bastan dos líneas, una para obtener el texto texto = pagina.extract_text() y otra mostrarlo print(texto) (5). La parte negativa es el escaso manejo que nos permite esta alternativa de afrontamiento de cuestiones complejas de forma y de contenidos del documento. La primera de estas limitaciones la veremos con cierto detalle cuando trabajemos con .pdf más complejos; la segunda requiere otro enfoque y otros medios (6).

De momento nos vamos a quedar con lo bueno, que todavía tiene recorrido. Trataremos estos temas en la próxima entrada y el acceso a documentos más complejos más adelante.

Documentos. pdf simple. Recuerda guardarlo en el directorio donde copies el script (7).

Notas:
1 No es que la extensión no afecte a cómo trabajar con PyPDF2, es que no supone un problema ni afecta al resultado del acceso. Veremos en esta misma entrada la diferencia entre trabajar con un documento de una sola página o del que queremos extraer el contenido de una página y extraer el contenido de un documento de múltiples páginas.
2 Muchas publicaciones se presentan a doble columna y contienen tablas de datos y elementos gráficos complejos. Puede que nos interese acceder a documentos de este tipo para extraer determinada información, aunque no sea esta la principal utilidad práctica que obtengamos de PyPDF2.
3 El que uso como recurso es un texto de un panel informativo del Museo de Burgos capturado mediante el scanner de la cámara del móvil. Posteriormente fue exportado como texto a un documento Writer y almacenado en formato .pdf. Muchos de estos textos simples resultan de procedimientos similares: elaborados originalmente con un procesador de texto, son guardados en formato .pdf para garantizar su integridad y su accesibilidad universal.
4 Donde se encuentra, por ejemplo, la información-diana, la que consideramos clave para nuestro objetivo.
5 La función print() escribe en consola el resultado de la captura asignada a la variable texto. Desde la consola podemos copiarlo y usarlo con otro servicio como un procesador de texto, por ejemplo, pero también guardarlo desde el script en un archivo de texto plano o en un documento Word creado mediante python-docx, por ejemplo.
6 Lo cierto es que esta supuesta limitación no lo es de PyPDF2; se trata en realidad de la segunda fase del procedimiento de acceso al contenido del .pdf, la cual no se puede ser resuelta mediante esta biblioteca.
7 Si modificas esta ubicación debes modificar también de la instrucción lector = PdfReader("VueltaCavernas.pdf")

domingo, 7 de septiembre de 2025

DATOS. Archivos PDF

Biblioteca PyPDF2 (I)

Acceso al documento

Aunque no nos vamos a limitar al acceso al contenido del documento .pdf, sí vamos a priorizar esta temática, aunque para hacerlo primero deberemos establecer nuestro objetivo, ya que no es lo mismo acceder para leer (y extraer datos) que hacerlo para escribir en el documento (1).

De momento nos planteamos acceder para leer, por lo que necesitamos importar el submódulo PdfReader como primera instrucción del script from PyPDF2 import PdfReader. El segundo paso consiste en asignar a una variable la ruta del archivo lector = PdfReader("pdf/VueltaCavernas.pdf"). El tercero consiste en acceder a las páginas, así que, dado que todo documento está compuesto por páginas, aunque el presente es muy simple y sólo consta de una única página, necesitamos acceder a la página para completar el acceso al documento. pagina = lector.pages[0].

Aunque hasta este momento si ejecutamos el script no vamos a obtener ninguna respuesta, lo cierto es que ya estamos "dentro" del .pdf, lo cual ya es mucho más de lo que podemos conseguir empleando otros medios (3).

Y de momento nos quedamos aquí porque el acceso real al contenido del documento va a depender del tipo de .pdf al que accedamos, como veremos en entradas posteriores.

Notas:
1Y a entraremos en más detalles, pero por el momento nos conformamos con aceptar esta información y el objetivo que se propone en la entrada.
2En caso de que el .pdf se encuentre en el mismo directorio que el script nos ahorramos el directorio.
3Para comprobar que estamos dentro, al final de este código podemos escribir una instrucción print("Hemos accedido al pdf") que nos permita confirmar que efectivamente hemos llegado a este punto.

DATOS. Archivos PDF

Biblioteca PyPDF2

Presentación

Muchas veces tenemos que trabajar con archivos .pdf. Si es para producirlos el único requisito es disponer de una utilidad de conversión, cosa que LibreOffice resuelve sin dificultad, incluyendo la transformación de un formulario Writer a formulario .pdf. Pero el problema se complica cuando tenemos que acceder al contenido de un documento .pdf. En estos casos la solución puede ser tan sencilla como copiar y pegar, pero otras veces esto no es posible y la cuestión se complica. Con frecuencia y por desgracia, las soluciones sencillas suelen ser también costosas en tiempo, lo que hace que en documentos complejos o extensos, en la práctica, no sean viables.

Con esta entrada inicio una subsección en la que explicaré cómo aplicar algunas soluciones mediante al acceso a los documentos .pdf mediante PyPDF2, biblioteca de Python que se instala directamente y que permite dividir, fusionar, recortar y transformar las páginas de ese tipo de archivos. También es posible agregar en ellos datos personalizados, opciones de visualización y contraseñas, así como extraer texto y los metadatos.

De todas las operaciones que nos permite realizar, muchas puede que tengan escaso interés para nosotros, aunque no está de más conocerlas. No obstante yo me centraré en explicar cómo usar PyPDF2 para aquello que mayor interés tiene en nuestro caso: extraer información del documento. Para ello debes instalarla pip install PyPDF2 desde Símbolo del sistema. Además te recomiendo es que visites su página web (1) y visualices algún que otro vídeo actualizado de You Tube (2).

Te dejo para que te entretengas con ello lo que necesites y para que practiques un poco con algunas de las opciones de esta biblioteca. En entradas posteriores realizaremos algunas prácticas.

Notas:

1También puedes visitar otras páginas que explican cómo realizar determinadas operaciones, pero es posible que no te funciones ese código debido a cambios de sintaxis entre versiones de PyPDF2. Es por ello mas recomendable aun que recurras a la web oficial de la biblioteca.

2Personalmente me ha parecido interesante este vídeo. Aunque supera los muy sencillos objetivos de esta entrada y hasta puede dar lugar a alguna confusión, lo cierto es que plantea temas interesantes que podríamos plantearnos en el futuro.