Trabajar con archivos PDF en Python: leer y dividir páginas

Este artículo es el primero de una serie sobre cómo trabajar con archivos PDF en Python: Lectura y división de páginas (usted está aquí) Adición de imágenes y marcas de agua Inserción, eliminación...

Este artículo es el primero de una serie sobre cómo trabajar con archivos PDF en Python:

El formato de documento PDF

Hoy en día, el formato de documento portátil (PDF) pertenece a los formatos de datos más utilizados. En 1990, Adobe definió la estructura de un documento PDF. La idea detrás del formato PDF es que los datos/documentos transmitidos se vean exactamente iguales para ambas partes involucradas en el proceso de comunicación: el creador, autor o remitente y el receptor. PDF es el sucesor del formato Posdata y estandarizado como [ISO 32000-2:2017](https://www.iso.org/standard/ 63534.html).

Procesamiento de documentos PDF {#procesamiento de documentos pdf}

Para Linux, hay poderosas herramientas de línea de comandos disponibles como pdftk y pdfgrep . Como desarrollador, existe una gran emoción al crear su propio software basado en Python y que utiliza bibliotecas PDF que están disponibles gratuitamente.

Este artículo es el comienzo de una pequeña serie y cubrirá estas útiles bibliotecas de Python. En la primera parte, nos centraremos en la manipulación de archivos PDF existentes. Aprenderá a leer y extraer el contenido (tanto texto como imágenes), rotar páginas individuales y dividir documentos en sus páginas individuales. La segunda parte cubrirá la adición de una marca de agua basada en superposiciones. La tercera parte se centrará exclusivamente en escribir/crear archivos PDF y también incluirá eliminar y volver a combinar páginas individuales en un nuevo documento.

Herramientas y bibliotecas

La variedad de soluciones disponibles para las herramientas, los módulos y las bibliotecas de PDF relacionados con Python es un poco confusa, y lleva un momento averiguar qué es qué y qué proyectos se mantienen continuamente. Según nuestra investigación, estos son los candidatos que están actualizados:

  • PyPDF2: una biblioteca de Python para extraer información y contenido de documentos, dividir documentos página por página, fusionar documentos, recortar páginas y agregar marcas de agua. PyPDF2 admite documentos cifrados y sin cifrar.

  • PDFMiner: Está escrito íntegramente en Python y funciona bien para Python 2.4. Para Python 3, use el paquete clonado PDFMiner.seis. Ambos paquetes le permiten analizar, analizar y convertir documentos PDF. Esto incluye la compatibilidad con PDF 1.7, así como con los idiomas CJK (chino, japonés y coreano) y varios tipos de fuentes (Type1, TrueType, Type3 y CID).

  • PDFConsulta: Se describe a sí mismo como "una biblioteca de scraping de PDF rápida y amigable" que se implementa como un envoltorio alrededor de PDFMiner, [lxml](/introduccion-a -la-biblioteca-lxml-de-python/), y pyquery. Su objetivo de diseño es "extraer datos de manera confiable de conjuntos de archivos PDF con la menor cantidad de código posible".

  • tabula-py: es un envoltorio Python simple de tabula-java, que puede leer tablas de PDF y convertirlas en pandas DataFrames. También le permite convertir un archivo PDF en un archivo CSV/TSV/JSON.

  • pdflib para Python: una extensión de la biblioteca Poppler que ofrece enlaces de Python. Le permite analizar, analizar y convertir documentos PDF. No debe confundirse con su colgante comercial que lleva el mismo nombre.

  • PyFPDF: Una biblioteca para la generación de documentos PDF bajo Python. Migrado de la biblioteca PHP FPDF, un reemplazo de la extensión PDFlib bien conocido con muchos ejemplos, scripts y derivados.

  • PDFTablas: Servicio comercial que ofrece extracción de tablas que vienen en formato PDF. Ofrece una API para que PDFTables se pueda usar como SAAS.

  • Píxide - el paquete de gráficos de Python: PyX es un paquete de Python para la creación de archivos PostScript, PDF y SVG. Combina una abstracción del modelo de dibujo PostScript con una interfaz TeX/LaTeX. Las tareas complejas, como la creación de gráficos 2D y 3D con calidad lista para publicación, se construyen a partir de estas primitivas.

  • ReportLab: una biblioteca ambiciosa de potencia industrial centrada principalmente en la creación precisa de documentos PDF. Disponible gratuitamente como una versión de código abierto, así como una versión comercial mejorada llamada ReportLab PLUS.

  • PyMuPDF (también conocido como "fitz"): Enlaces de Python para MuPDF, que es un visor ligero de PDF y XPS. La biblioteca puede acceder a archivos en formatos PDF, XPS, OpenXPS, epub, cómics y libros de ficción, y es conocida por su alto rendimiento y alta calidad de representación.

  • pdfrw: un analizador de PDF puro basado en Python para leer y escribir PDF. Reproduce fielmente formatos vectoriales sin rasterización. Junto con ReportLab, ayuda a reutilizar partes de PDF existentes en nuevos PDF creados con ReportLab.

Biblioteca utilizada para


Lectura de PyPDF2 Lectura PyMuPDF lectura de pdflib PDFLectura de tablas tabula-py Lectura Lectura de PDFMiner.six Lectura de consultas PDF pdfrw Leer, escribir/crear Redacción/Creación de Reportlab PyX Escritura/Creación PyFPDF Escritura/Creación

A continuación, nos centraremos en PyPDF2 y PyMuPDF, y explicaremos cómo extraer texto e imágenes de la forma más sencilla posible. Para comprender el uso de PyPDF2, ayudó una combinación de la documentación oficial y muchos ejemplos que están disponibles en otros recursos. Por el contrario, la documentación oficial de PyMuPDF es mucho más clara y considerablemente más rápida usando la biblioteca.

Extracción de texto con PyPDF2

PyPDF2 se puede instalar como un paquete de software regular o usando pip3 (para Python3). Las pruebas aquí se basan en el paquete para la próxima versión 10 "Buster" de Debian GNU/Linux. El nombre del paquete Debian es python3-pypdf2.

Listado 1 importa la clase PdfFileReader, primero. Luego, usando esta clase, abre el documento y extrae la información del documento usando el método getDocumentInfo(), el número de páginas usando getDocumentInfo() y el contenido de la primera página.

Tenga en cuenta que PyPDF2 comienza a contar las páginas con 0, y es por eso que la llamada pdf.getPage(0) recupera la primera página del documento. Eventualmente, la información extraída se imprime en stdout.

Listado 1: Extrayendo la información y el contenido del documento.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/python

from PyPDF2 import PdfFileReader

pdf_document = "example.pdf"
with open(pdf_document, "rb") as filehandle:
    pdf = PdfFileReader(filehandle)
    info = pdf.getDocumentInfo()
    pages = pdf.getNumPages()

    print (info)
    print ("number of pages: %i" % pages)

    page1 = pdf.getPage(0)
    print(page1)
    print(page1.extractText())

Higo. 1: Texto extraído de un archivo PDF usando PyPDF2{.img-responsive}

[Higo. 1: Texto extraído de un archivo PDF usando PyPDF2]{.small}

Como se muestra en la Figura 1 anterior, el texto extraído se imprime de forma continua. No hay párrafos, ni separaciones de oraciones. Como se indica en la documentación de PyPDF2, todos los datos de texto se devuelven en el orden en que se proporcionan en el flujo de contenido de la página, y confiar en ellos puede generar algunas sorpresas. Esto depende principalmente de la estructura interna del documento PDF y de cómo el proceso de escritura de PDF produjo el flujo de instrucciones PDF.

Extracción de texto con PyMuPDF

PyMuPDF está disponible en el sitio web de PyPi e instala el paquete con el siguiente comando en una terminal:

1
$ pip3 install PyMuPDF

La visualización de la información del documento, la impresión del número de páginas y la extracción del texto de un documento PDF se realizan de forma similar a PyPDF2 (consulte Listado 2). El módulo a importar se llama fitz y se remonta al nombre anterior de PyMuPDF.

Listado 2: Extracción de contenido de un documento PDF usando PyMuPDF.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/python

import fitz

pdf_document = "example.pdf"
doc = fitz.open(pdf_document):
print ("number of pages: %i" % doc.pageCount)
print(doc.metadata)

page1 = doc.loadPage(0)
page1text = page1.getText("text")
print(page1text)

Lo bueno de PyMuPDF es que mantiene intacta la estructura del documento original: los párrafos completos con saltos de línea se mantienen tal como están en el documento PDF (ver Figura 2).

Higo. 2: Datos de texto extraídos{.img-responsive}

[Higo. 2: Datos de texto extraídos]{.small}

Extraer imágenes de archivos PDF con PyMuPDF

PyMuPDF simplifica la extracción de imágenes de documentos PDF utilizando el método getPageImageList(). El listado 3 se basa en un ejemplo de la página wiki de PyMuPDF y extrae y guarda todas las imágenes del PDF como archivos PNG página por página. Si una imagen tiene un espacio de color CMYK, primero se convertirá a RGB.

Listado 3: Extrayendo imágenes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/python

import fitz

pdf_document = fitz.open("file.pdf")
for current_page in range(len(pdf_document)):
    for image in pdf_document.getPageImageList(current_page):
        xref = image[0]
        pix = fitz.Pixmap(pdf_document, xref)
        if pix.n < 5:        # this is GRAY or RGB
            pix.writePNG("page%s-%s.png" % (current_page, xref))
        else:                # CMYK: convert to RGB first
            pix1 = fitz.Pixmap(fitz.csRGB, pix)
            pix1.writePNG("page%s-%s.png" % (current_page, xref))
            pix1 = None
        pix = None

Al ejecutar este script de Python en un PDF de 400 páginas, extrajo 117 imágenes en menos de 3 segundos, lo cual es sorprendente. Las imágenes individuales se almacenan en formato PNG. Para mantener el formato y el tamaño de la imagen original, en lugar de convertirla a PNG, eche un vistazo a las versiones extendidas de los scripts en wiki PyMuPDF.

Higo. 3: Imágenes extraídas{.img-responsive}

[Higo. 3: Imágenes extraídas]{.pequeño}

Dividir archivos PDF en páginas con PyPDF2

Para este ejemplo, primero se deben importar las clases PdfFileReader y PdfFileWriter. Luego abrimos el archivo PDF, creamos un objeto lector y recorremos todas las páginas usando el método getNumPages del objeto lector.

Dentro del bucle for, creamos una nueva instancia de PdfFileWriter, que aún no contiene ninguna página. Luego agregamos la página actual a nuestro objeto escritor usando el método pdfWriter.addPage(). Este método acepta un objeto de página, que obtenemos usando el método PdfFileReader.getPage().

El siguiente paso es crear un nombre de archivo único, lo que hacemos usando el nombre de archivo original más la palabra "página", más el número de página. Agregamos 1 al número de página actual porque PyPDF2 cuenta los números de página que comienzan en cero.

Finalmente, abrimos el nuevo nombre de archivo en modo "escribir binario" (modo wb), y usamos el método write() de la clase pdfWriter para guardar la página extraída en el disco.

Listado 4: Dividir un PDF en páginas individuales.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/usr/bin/python

from PyPDF2 import PdfFileReader, PdfFileWriter

pdf_document = "example.pdf"
pdf = PdfFileReader(pdf_document)

for page in range(pdf.getNumPages()):
    pdf_writer = PdfFileWriter
    current_page = pdf.getPage(page)
    pdf_writer.addPage(current_page)

    outputFilename = "example-page-{}.pdf".format(page + 1)
    with open(outputFilename, "wb") as out:
        pdf_writer.write(out)

        print("created", outputFilename)

Higo. 4: Dividir en PDF{.img-responsive}

[Higo. 4: Dividir un PDF]{.small}

Buscar todas las páginas que contengan texto

Este caso de uso es bastante práctico y funciona de manera similar a pdfgrep. Usando PyMuPDF, el script devuelve todos los números de página que contienen la cadena de búsqueda dada. Las páginas se cargan una tras otra y, con la ayuda del método searchFor(), se detectan todas las ocurrencias de la cadena de búsqueda. En caso de coincidencia, se imprime un mensaje correspondiente en stdout.

Listado 5: Buscar un texto dado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/python

import fitz

filename = "example.pdf"
search_term = "invoice"
pdf_document = fitz.open(filename):

for current_page in range(len(pdf_document)):
    page = pdf_document.loadPage(current_page)
    if page.searchFor(search_term):
        print("%s found on page %i" % (search_term, current_page))

La Figura 5 a continuación muestra el resultado de la búsqueda del término "Debian GNU/Linux" en un libro de 400 páginas.

Higo. 5: Búsqueda en documento PDF{.img-responsive}

[Higo. 5: Búsqueda de un documento PDF]{.small}

Conclusión

Los métodos que se muestran aquí son bastante poderosos. Con un número comparativamente pequeño de líneas de código, se obtiene fácilmente un resultado. Se examinan más casos de uso en la Parte Dos (¡próximamente!) que cubre cómo agregar una marca de agua a un PDF.