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

时间:2014-04-06 18:31:05

标签: python pdf pdfminer

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

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

鉴于PDF文件,输出应如下所示:

   489, 41,  "Signature"
   500, 52,  "b"
   630, 202, "a_g_i_r"

2 个答案:

答案 0 :(得分:33)

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

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)

答案 1 :(得分:9)

这是一个可复制粘贴的示例,该示例列出了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))

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

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

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

  

在实际的PDF文件中,文本部分可能会在其运行过程中分成几个块,具体取决于编写软件。因此,文本提取需要拼接文本块。

像大多数PDFMiner一样,

LAParams的参数是未记录的,但是您可以在in the source code或通过在Python shell上调用help(LAParams)来查看它们。参数 some 的含义在https://pdfminer-docs.readthedocs.io/pdfminer_index.html#pdf2txt-py中给出,因为它们也可以在命令行中作为参数传递给pdf2text

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

  • LTTextBox
  • LTFigure
  • LTImage
  • LTLine
  • LTRect

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

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

Tree diagram of the structure of an <code>LTPage</code>. Of relevance to this answer: it shows that an <code>LTPage</code> contains the 5 types listed above, and that an <code>LTTextBox</code> contains <code>LTTextLine</code>s plus unspecified other stuff, and that an <code>LTTextLine</code> contains <code>LTChar</code>s, <code>LTAnno</code>s, <code>LTText</code>s, and unspecified other stuff.

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

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

除了bbox外,LTTextBox还具有.get_text()方法,如上所示,该方法以字符串形式返回其文本内容。请注意,每个LTTextBoxLTChar(由PDF明确绘制的字符,带有bbox)和LTAnno(由PDFMiner添加到字符串的多余空格)组成的集合文本框内容的表示形式取决于字符之间的距离;这些字符没有bbox

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

最后,值得注意的是,不同于上面引用的其他Stack Overflow答案,我不必费心递归到LTFigure s中。尽管LTFigure可以包含文本,但PDFMiner似乎无法将文本分组为LTTextBox es(您可以尝试使用https://stackoverflow.com/a/27104504/1709587的示例PDF),而是生成{{ 1}}直接包含LTFigure个对象。从原则上讲,您可以弄清楚如何将它们组合成一个字符串,但是PDFMiner(从20181108版开始)无法为您完成。

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