【问题标题】:How can I extract text fragments from PDF with their coordinates in Python?如何从 PDF 中提取文本片段及其在 Python 中的坐标?
【发布时间】:2020-11-20 00:27:00
【问题描述】:

给定一个数字创建的 PDF 文件,我想提取带有坐标的文本。边界框会很棒,但是锚点+字体/字体大小也可以。

我创建了一个example PDF document,以便轻松尝试/分享结果。

我尝试过的

pdftotext

pdftotext PDF-export-example.pdf -layout

给出this output。它已包含文本,但坐标不在其中。

PyPDF2

PyPDF2 更糟糕 - 既不是坐标,也不是字体大小,在这种情况下,甚至 ASCII 艺术线索都没​​有:

from PyPDF2 import PdfFileReader


def text_extractor(path):
    with open(path, "rb") as f:
        pdf = PdfFileReader(f)
        page = pdf.getPage(0)
        text = page.extractText()
        print(text)


if __name__ == "__main__":
    path = "PDF-export-example.pdf"
    text_extractor(path)

pdfminer.six

另一种提取文本的方法,但没有坐标/字体大小。 感谢Duck puncher 的这个:

from io import StringIO

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


def convert_pdf_to_txt(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    codec = "utf-8"
    laparams = LAParams()
    device = TextConverter(rsrcmgr, retstr, codec=codec, laparams=laparams)
    fp = open(path, "rb")
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    password = ""
    maxpages = 0
    caching = True
    pagenos = set()

    for page in PDFPage.get_pages(
        fp,
        pagenos,
        maxpages=maxpages,
        password=password,
        caching=caching,
        check_extractable=True,
    ):
        interpreter.process_page(page)

    text = retstr.getvalue()

    fp.close()
    device.close()
    retstr.close()
    return text


if __name__ == "__main__":
    print(convert_pdf_to_txt("PDF-export-example.pdf"))

这个方向更正确,因为它可以给出字体名称和大小。但是坐标仍然丢失(并且输出有点冗长,因为它是逐个字符的):

from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer, LTChar

for page_layout in extract_pages("PDF-export-example.pdf"):
    for element in page_layout:
        if isinstance(element, LTTextContainer):
            for text_line in element:
                for character in text_line:
                    if isinstance(character, LTChar):
                        print(character)
                        print(character.fontname)
                        print(character.size)

表格-py

这里我什么都没有:

from tabula import read_pdf

df = read_pdf("PDF-export-example.pdf")
print(df)

【问题讨论】:

    标签: python pdf


    【解决方案1】:

    我使用PyMuPDF 将页面内容提取为带有 bbox 信息的单个单词列表。

    import fitz
    
    doc = fitz.open("PDF-export-example.pdf")
    
    for page in doc:
        wlist = page.getTextWords()  # make the word list
        print(wlist)
    

    输出

    [
        (72.0250015258789, 72.119873046875, 114.96617889404297, 106.299560546875, 'Test', 0, 0, 0),
        (120.26901245117188, 72.119873046875, 231.72618103027344, 106.299560546875, 'document', 0, 0, 1),
        (72.0250015258789, 106.21942138671875, 101.52294921875, 120.18524169921875, 'Lorem', 1, 0, 0),
        (103.98699951171875, 106.21942138671875, 132.00445556640625, 120.18524169921875, 'ipsum', 1, 0, 1),
        (134.45799255371094, 106.21942138671875, 159.06637573242188, 120.18524169921875, 'dolor', 1, 0, 2),
        (161.40098571777344, 106.21942138671875, 171.95208740234375, 120.18524169921875, 'sit', 1, 0, 3),
        ...
    ]

    page.getTextWords()

    • 方法使用空格和行将页面的文本分成“单词” 分隔符作为分隔符。因此,此列表中的单词不包含 空格或换行符。

    • 返回类型:list

    此列表中的一项如下所示:

    (x0, y0, x1, y1, "word", block_no, line_no, word_no)
    

    其中前 4 项是单词 bbox 的浮点坐标。最后三个整数提供了有关单词下落的更多信息。


    关于名字 fitz 的注释
    PyMuPDF 库的标准 Python 导入语句是 import fitz。这是有历史原因的:

    MuPDF 的原始渲染库名为 Libart。

    Artifex Software 收购 MuPDF 项目后,开发重点转移到编写一个名为 Fitz 的新现代图形库。 Fitz 最初的目的是作为一个研发项目来取代老化的 Ghostscript 图形库,但现在却成为了支持 MuPDF 的渲染引擎。

    【讨论】:

    • 哇,这太棒了!你知道是否也可以获得字体/字体大小?
    • 很好的答案。你还能说出这些坐标值到底是什么吗?我的意思是这些像素值还是其他比例?我看到它们是浮点数,所以我不认为它是像素值。我想将它作为图像显示在 PDF 上。为此使用 OpenCV
    • @RishabhGupta 看看这个。 pymupdf.readthedocs.io/en/latest/rect.html#rect
    【解决方案2】:

    您可以使用-bbox option 解析poppler 的pdftotext 的输出:

    import subprocess
    from lxml import etree
    
    file = 'PDF-export-example.pdf'
    xml = etree.fromstring(subprocess.check_output(['pdftotext', '-bbox', file , '-']))
    for pn, page in enumerate(xml.findall('.//{http://www.w3.org/1999/xhtml}page')):
        for word in page.findall('{http://www.w3.org/1999/xhtml}word'):
            print(pn, word.get('xMin'), word.get('yMin'),
                word.get('xMax'), word.get('yMax'), word.text)
    

    输出:

    0 72.025000 72.124000 114.977000 105.780000 Test
    0 120.269000 72.124000 231.737000 105.780000 document
    0 72.025000 106.220500 101.519500 119.755000 Lorem
    0 103.987000 106.220500 132.001000 119.755000 ipsum
    0 134.458000 106.220500 159.070000 119.755000 dolor
    ...
    

    【讨论】:

    • 请问,如何使用您的解决方案“etree”获取页面的宽度和高度?
    • @DataScientist etree 用于解析pdftotext 的输出,用于文本提取,不显示页面大小。有关如何获取页面大小的信息,请参阅stackoverflow.com/questions/6230752/…
    猜你喜欢
    • 2022-10-15
    • 1970-01-01
    • 2014-05-18
    • 2023-03-11
    • 2021-01-07
    • 2011-09-05
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多