Saltar al contenido

¿Cómo extraer texto y coordenadas de texto de un archivo PDF?

Si te encuentras con alguna parte que no comprendes puedes comentarlo y te responderemos tan rápido como podamos.

Solución:

Las líneas nuevas se convierten en guiones bajos en la salida final. Esta es la solución de trabajo mínima que encontré.

from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.pdfdevice import PDFDevice
from pdfminer.layout import LAParams
from pdfminer.converter import PDFPageAggregator
import pdfminer

# Open a PDF file.
fp = open('/Users/me/Downloads/test.pdf', 'rb')

# Create a PDF parser object associated with the file object.
parser = PDFParser(fp)

# Create a PDF document object that stores the document structure.
# Password for initialization as 2nd parameter
document = PDFDocument(parser)

# Check if the document allows text extraction. If not, abort.
if not document.is_extractable:
    raise PDFTextExtractionNotAllowed

# Create a PDF resource manager object that stores shared resources.
rsrcmgr = PDFResourceManager()

# Create a PDF device object.
device = PDFDevice(rsrcmgr)

# BEGIN LAYOUT ANALYSIS
# Set parameters for analysis.
laparams = LAParams()

# Create a PDF page aggregator object.
device = PDFPageAggregator(rsrcmgr, laparams=laparams)

# Create a PDF interpreter object.
interpreter = PDFPageInterpreter(rsrcmgr, device)

def parse_obj(lt_objs):

    # loop over the object list
    for obj in lt_objs:

        # if it's a textbox, print text and location
        if isinstance(obj, pdfminer.layout.LTTextBoxHorizontal):
            print "%6d, %6d, %s" % (obj.bbox[0], obj.bbox[1], obj.get_text().replace('n', '_'))

        # if it's a container, recurse
        elif isinstance(obj, pdfminer.layout.LTFigure):
            parse_obj(obj._objs)

# loop over all pages in the document
for page in PDFPage.create_pages(document):

    # read the page into a layout object
    interpreter.process_page(page)
    layout = device.get_result()

    # extract text from this object
    parse_obj(layout._objs)

Aquí hay un ejemplo listo para copiar y pegar que enumera las esquinas superiores izquierdas de cada bloque de texto en un PDF, y que creo que debería funcionar para cualquier PDF que no incluya “Form XObjects” que contengan texto:

from pdfminer.layout import LAParams, LTTextBox
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator

fp = open('yourpdf.pdf', 'rb')
rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
pages = PDFPage.get_pages(fp)

for page in pages:
    print('Processing next page...')
    interpreter.process_page(page)
    layout = device.get_result()
    for lobj in layout:
        if isinstance(lobj, LTTextBox):
            x, y, text = lobj.bbox[0], lobj.bbox[3], lobj.get_text()
            print('At %r is text: %s' % ((x, y), text))

El código anterior se basa en el ejemplo de Realizar análisis de diseño en los documentos de PDFMiner, además de los ejemplos de pnj (https://stackoverflow.com/a/22898159/1709587) y Matt Swain (https://stackoverflow.com/a/ 25262470/1709587). Hay un par de cambios que hice a partir de estos ejemplos anteriores:

  • yo suelo PDFPage.get_pages(), que es una forma abreviada de crear un documento, verificándolo is_extractabley pasárselo a PDFPage.create_pages()
  • No me molesto en manejar LTFigures, ya que PDFMiner actualmente no es capaz de manejar el texto dentro de ellos de forma limpia.

LAParams le permite establecer algunos parámetros que controlan cómo los caracteres individuales en el PDF se agrupan mágicamente en líneas y cuadros de texto por PDFMiner. Si le sorprende que tal agrupación sea algo que debe suceder, está justificado en los documentos pdf2txt:

En un archivo PDF real, las partes de texto se pueden dividir en varios fragmentos en medio de su ejecución, según el software de creación. Por lo tanto, la extracción de texto debe empalmar fragmentos de texto.

LAParamsLos parámetros, como la mayoría de PDFMiner, no están documentados, pero puede verlos en el código fuente o llamando help(LAParams) en su shell de Python. El significado de algunos de los parámetros se proporciona en https://pdfminer-docs.readthedocs.io/pdfminer_index.html#pdf2txt-py ya que también se pueden pasar como argumentos a pdf2text en la línea de comando.

los layout el objeto de arriba es un LTPage, que es un iterable de “objetos de diseño”. Cada uno de estos objetos de diseño puede ser uno de los siguientes tipos …

  • LTTextBox
  • LTFigure
  • LTImage
  • LTLine
  • LTRect

… o sus subclases. (En particular, sus cuadros de texto probablemente estarán LTTextBoxHorizontals.)

Más detalle de la estructura de un LTPage se muestra en esta imagen de los documentos:

Tree diagram of the structure of an <code>
<div class=

LTPage. De relevancia para esta respuesta: muestra que un LTPage contiene los 5 tipos enumerados anteriormente, y que un LTTextBox contiene LTTextLines más otras cosas no especificadas, y que un LTTextLine contiene LTChars, LTAnnos, LTTexts, y otras cosas no especificadas. “src =” https://i.stack.imgur.com/9aDTv.png “/>

Cada uno de los tipos anteriores tiene un .bbox propiedad que tiene un (x0, y0, x1, y1) tupla que contiene las coordenadas de la izquierda, la parte inferior, la derecha y la parte superior del objeto, respectivamente. Las coordenadas y se dan como la distancia desde el fondo de la página. Si le resulta más conveniente trabajar con el eje y que va de arriba a abajo, puede restarlos de la altura de la página. .mediabox:

x0, y0_orig, x1, y1_orig = some_lobj.bbox
y0 = page.mediabox[3] - y1_orig
y1 = page.mediabox[3] - y0_orig

Además de un bbox, LTTextBoxes también tiene un .get_text() método, que se muestra arriba, que devuelve su contenido de texto como un string. Tenga en cuenta que cada LTTextBox es una colección de LTChars (caracteres dibujados explícitamente por el PDF, con un bbox) y LTAnnos (espacios adicionales que PDFMiner agrega al string representación del contenido del cuadro de texto basada en los caracteres dibujados a gran distancia; estos no tienen bbox).

El ejemplo de código al comienzo de esta respuesta combinó estas dos propiedades para mostrar las coordenadas de cada bloque de texto.

Finalmente, vale la pena señalar que diferente a las otras respuestas de Stack Overflow citadas anteriormente, no me molesto en recurrir a LTFigures. A pesar de que LTFigures pueden contener texto, PDFMiner no parece capaz de agrupar ese texto en LTTextBoxes (puede probar usted mismo en el PDF de ejemplo de https://stackoverflow.com/a/27104504/1709587) y en su lugar produce un LTFigure que contiene directamente LTChar objetos. En principio, podría averiguar cómo juntarlos en un string, pero PDFMiner (a partir de la versión 20181108) no puede hacerlo por usted.

Sin embargo, con suerte, los PDF que necesita analizar no usan Form XObjects con texto en ellos, por lo que esta advertencia no se aplicará a usted.

Ten en cuenta compartir esta división si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *