如何从PDF文件中提取文本和文本坐标?

pnj*_*pnj 21 python pdf pdfminer

我想用PDFMiner从PDF文件中提取所有文本框和文本框坐标.

许多其他Stack Overflow帖子解决了如何以有序方式提取所有文本,但是如何进行获取文本和文本位置的中间步骤?

给定一个PDF文件,输出应该类似于:

   489, 41,  "Signature"
   500, 52,  "b"
   630, 202, "a_g_i_r"
Run Code Online (Sandbox Code Playgroud)

pnj*_*pnj 36

新行在最终输出中转换为下划线.这是我发现的最小工作解决方案.

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)
Run Code Online (Sandbox Code Playgroud)

  • 我留下了我自己的答案,以几种方式对此进行了调整。您在此处创建的第一个“设备”从未使用过,并且可以使用“get_pages”缩短设置解析的初始 cruft。我特别想知道:你有没有发现递归到 LTFigure 有效的情况?我自己的实验告诉我,其中的文本*不会*被 PDFMiner 分组到文本框对象中,因此您在这里递归到它们将永远不会起作用。 (3认同)
  • 来自 Google 的任何人几乎肯定会想要底部的最新答案,这是非常优雅的 /sf/answers/4840582421/ (2认同)

Pie*_*ter 27

完全披露,我是pdfminer.6的维护者之一。它是 pdfminer for python 3 的社区维护版本。

如今,pdfminer.6 有多个 API 可以从 PDF 中提取文本和信息。对于以编程方式提取信息,我建议使用extract_pages(). 这允许您检查页面上的所有元素,这些元素按布局算法创建的有意义的层次结构排序。

以下示例是显示层次结构中所有元素的 Python 方式。它使用pdfminer.6 的示例目录中的simple1.pdf 。

from pathlib import Path
from typing import Iterable, Any

from pdfminer.high_level import extract_pages


def show_ltitem_hierarchy(o: Any, depth=0):
    """Show location and text of LTItem and all its descendants"""
    if depth == 0:
        print('element                        x1  y1  x2  y2   text')
        print('------------------------------ --- --- --- ---- -----')

    print(
        f'{get_indented_name(o, depth):<30.30s} '
        f'{get_optional_bbox(o)} '
        f'{get_optional_text(o)}'
    )

    if isinstance(o, Iterable):
        for i in o:
            show_ltitem_hierarchy(i, depth=depth + 1)


def get_indented_name(o: Any, depth: int) -> str:
    """Indented name of LTItem"""
    return '  ' * depth + o.__class__.__name__


def get_optional_bbox(o: Any) -> str:
    """Bounding box of LTItem if available, otherwise empty string"""
    if hasattr(o, 'bbox'):
        return ''.join(f'{i:<4.0f}' for i in o.bbox)
    return ''


def get_optional_text(o: Any) -> str:
    """Text of LTItem if available, otherwise empty string"""
    if hasattr(o, 'get_text'):
        return o.get_text().strip()
    return ''


path = Path('~/Downloads/simple1.pdf').expanduser()

pages = extract_pages(path)
show_ltitem_hierarchy(pages)
Run Code Online (Sandbox Code Playgroud)

输出显示层次结构中的不同元素。每个的边界框。以及该元素包含的文本。

element                        x1  y1  x2  y2   text
------------------------------ --- --- --- ---- -----
generator                       
  LTPage                       0   0   612 792  
    LTTextBoxHorizontal        100 695 161 719  Hello
      LTTextLineHorizontal     100 695 161 719  Hello
        LTChar                 100 695 117 719  H
        LTChar                 117 695 131 719  e
        LTChar                 131 695 136 719  l
        LTChar                 136 695 141 719  l
        LTChar                 141 695 155 719  o
        LTChar                 155 695 161 719  
        LTAnno                  
    LTTextBoxHorizontal        261 695 324 719  World
      LTTextLineHorizontal     261 695 324 719  World
        LTChar                 261 695 284 719  W
        LTChar                 284 695 297 719  o
        LTChar                 297 695 305 719  r
        LTChar                 305 695 311 719  l
        LTChar                 311 695 324 719  d
        LTAnno                  
    LTTextBoxHorizontal        100 595 161 619  Hello
      LTTextLineHorizontal     100 595 161 619  Hello
        LTChar                 100 595 117 619  H
        LTChar                 117 595 131 619  e
        LTChar                 131 595 136 619  l
        LTChar                 136 595 141 619  l
        LTChar                 141 595 155 619  o
        LTChar                 155 595 161 619  
        LTAnno                  
    LTTextBoxHorizontal        261 595 324 619  World
      LTTextLineHorizontal     261 595 324 619  World
        LTChar                 261 595 284 619  W
        LTChar                 284 595 297 619  o
        LTChar                 297 595 305 619  r
        LTChar                 305 595 311 619  l
        LTChar                 311 595 324 619  d
        LTAnno                  
    LTTextBoxHorizontal        100 495 211 519  H e l l o
      LTTextLineHorizontal     100 495 211 519  H e l l o
        LTChar                 100 495 117 519  H
        LTAnno                  
        LTChar                 127 495 141 519  e
        LTAnno                  
        LTChar                 151 495 156 519  l
        LTAnno                  
        LTChar                 166 495 171 519  l
        LTAnno                  
        LTChar                 181 495 195 519  o
        LTAnno                  
        LTChar                 205 495 211 519  
        LTAnno                  
    LTTextBoxHorizontal        321 495 424 519  W o r l d
      LTTextLineHorizontal     321 495 424 519  W o r l d
        LTChar                 321 495 344 519  W
        LTAnno                  
        LTChar                 354 495 367 519  o
        LTAnno                  
        LTChar                 377 495 385 519  r
        LTAnno                  
        LTChar                 395 495 401 519  l
        LTAnno                  
        LTChar                 411 495 424 519  d
        LTAnno                  
    LTTextBoxHorizontal        100 395 211 419  H e l l o
      LTTextLineHorizontal     100 395 211 419  H e l l o
        LTChar                 100 395 117 419  H
        LTAnno                  
        LTChar                 127 395 141 419  e
        LTAnno                  
        LTChar                 151 395 156 419  l
        LTAnno                  
        LTChar                 166 395 171 419  l
        LTAnno                  
        LTChar                 181 395 195 419  o
        LTAnno                  
        LTChar                 205 395 211 419  
        LTAnno                  
    LTTextBoxHorizontal        321 395 424 419  W o r l d
      LTTextLineHorizontal     321 395 424 419  W o r l d
        LTChar                 321 395 344 419  W
        LTAnno                  
        LTChar                 354 395 367 419  o
        LTAnno                  
        LTChar                 377 395 385 419  r
        LTAnno                  
        LTChar                 395 395 401 419  l
        LTAnno                  
        LTChar                 410 395 424 419  d
        LTAnno                  
Run Code Online (Sandbox Code Playgroud)

(类似的答案 在这里这里这里 ,我会尽力让它们保持同步。)

  • 那真是太美了,太棒了! (9认同)

Mar*_*ery 13

这是一个可复制粘贴的示例,该示例列出了PDF中每个文本块的左上角,我认为该示例适用于其中不包含“ Form XObjects”的任何PDF:

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))
Run Code Online (Sandbox Code Playgroud)

上面的代码基于PDFMiner文档中的Performing Layout Analysis示例,以及pnj(/sf/answers/1602871161/)和Matt Swain(https://stackoverflow.com/a/ 25262470/1709587)。这些先前的示例对我进行了一些更改:

  • 我使用PDFPage.get_pages(),这是创建文档,检查文档is_extractable并将其传递给的快捷方式PDFPage.create_pages()
  • 我不必理会LTFigures,因为PDFMiner当前无法正常处理其中的文本。

LAParams使您可以设置一些参数,以控制PDFMiner将PDF中的各个字符如何神奇地分组为行和文本框。如果您对这样的分组根本没有必要发生感到惊讶,请在pdf2txt docs中找到理由:

在实际的PDF文件中,根据编写软件的不同,文本部分可能会在运行过程中分成几个块。因此,文本提取需要拼接文本块。

LAParams像大多数PDFMiner一样,其参数是未记录的,但是您可以在源代码中或通过help(LAParams)在Python Shell 上调用来查看它们。一些参数的含义在https://pdfminer-docs.readthedocs.io/pdfminer_index.html#pdf2txt-py中给出,因为它们也可以作为参数传递给pdf2text命令行。

layout上面的对象是LTPage,可以迭代“布局对象”。这些布局对象均可以是以下类型之一...

  • LTTextBox
  • LTFigure
  • LTImage
  • LTLine
  • LTRect

...或其子类。(特别是,您的文本框可能全部为LTTextBoxHorizontal。)

LTPage文档中的此图片显示了的结构的更多详细信息:

<code> LTPage </ code>的结构树图。 与该答案的相关性:它表明<code> LTPage </ code>包含上面列出的5种类型,并且<code> LTTextBox </ code>包含<code> LTTextLine </ code>以及未指定的其他内容,并且<code> LTTextLine </ code>包含<code> LTChar </ code> s,<code> LTAnno </ code>,<code> LTText </ code>和未指定的其他内容。

上面的每种类型都有一个.bbox属性,该属性保存一个(x0y0x1y1)元组,分别包含对象的左,下,右和上坐标。y坐标表示为距页面底部的距离。如果使用y轴从上到下更方便,则可以从页面的高度中减去它们.mediabox

x0, y0, x1, y1 = some_lobj.bbox
y0 = page.mediabox[3] - y1
y1 = page.mediabox[3] - y0
Run Code Online (Sandbox Code Playgroud)

除之外bboxLTTextBoxes还具有.get_text()上面显示的方法,该方法以字符串形式返回其文本内容。请注意,每个LTTextBox集合都是LTChars(由PDF明确绘制的字符,带有bbox)和LTAnnos(PDFMiner根据距离很远的字符添加到文本框内容的字符串表示形式的额外空格)的集合;这些没有bbox)。

此答案开头的代码示例结合了这两个属性,以显示每个文本块的坐标。

最后,值得注意的是,上面引用的其他Stack Overflow答案不同,我不必费心递归到LTFigures。尽管LTFigures可以包含文本,但PDFMiner似乎无法将文本分组为LTTextBoxes(您可以从/sf/answers/1897315311/尝试使用示例PDF ),而是生成一个LTFigure直接包含LTChar对象的。原则上,您可以弄清楚如何将它们组合成一个字符串,但是PDFMiner(从20181108版开始)无法为您完成此工作。

但是,希望您需要解析的PDF不会使用带有文本的Form XObject,因此此警告将不适用于您。