如何使用Python从PDF文件中提取带删除线的文本

wvmv3b1j  于 2022-11-27  发布在  Python
关注(0)|答案(2)|浏览(210)

我目前正在尝试从大量PDF表单中提取信息,例如:

文本“female”应该在这里被提取出来。所以与我的标题相反,我实际上是在尝试提取没有删除线的文本,而不是有删除线的文本。但是如果我能识别哪些单词有删除线,我就可以很容易地识别出相反的情况。
this post获得灵感后,我想出了这组代码:

import os
import glob
from pdf2docx import parse
from docx import Document

lst = []

files = glob.glob(os.getcwd() + r'\PDFs\*.pdf')

for i in range(len(files)):
    filename = files[i].split('\\')[-1].split('.')[-2]
    parse(files[i])
    document = Document(os.getcwd() + rf'\PDFs\{filename}.docx')
    for p in document.paragraphs:
        for run in p.runs:
            if run.font.strike:
                lst.append(run.text)
    os.remove(os.getcwd() + rf'\PDFs\{filename}.docx')

上面的代码所做的是将我所有的PDF文件转换为Word文档(docx),然后在Word文档中搜索带有删除线的文本,提取这些文本,然后删除Word文档。
正如你可能已经正确地怀疑,这组代码是非常缓慢和低效的,需要大约30秒来运行我的4个PDF样本集不到10页的组合。
我不相信这是最好的方法。然而,当我在网上做了一些研究,pdf2docx extracts data from PDFs using PyMuPDF,但还PyMuPDF do not come with the capability to recognise strikethroughs in PDF text。这怎么可能呢?当pdf 2docx可以完美地将PDF中的删除线转换为docx文档时,表明删除线在某种程度上被识别。
总之,我想征求意见,是否有可能在PDF中使用Python提取带删除线的文本。谢谢!

tf7tbtn2

tf7tbtn21#

**免责声明:**我是borb的作者,库建议在此回答

最后,具体的代码将根据删除线在PDF中的实现方式而变化。请允许我澄清一下:
PDF文档(通常)没有结构的概念。因此,虽然我们可能会看到一段文本,由几行文本组成,PDF(大部分)只包含渲染指令。
例如:
1.转到X、Y
1.将当前字体设置为Helvetica-Bold
1.将当前颜色设置为黑色
1.画字母“H”
1.转到X、Y(这次稍微向右移动)
1.画字母“e”
1.等等
因此,在所有的可能性,文本是删除线通过没有标记为这样在任何有意义的方式。
我认为有两个选择:

  1. PDF具有注解的概念。注解通常是添加到页面顶部的内容片段。这些内容片段可以是额外的文本、几何图形等。有特定的注解用于删除线。
    1.它可能是一个注解,但也可能是一个简单地出现在文本上的几何图形(在本例中是一条线)。
    1.它可能是一个绘制指令(在页面内容流中),只是在文本上呈现一条黑线。
    您的PDF可能包含一个(或多个)删除线,这取决于最初创建删除线的软件。
    您可以使用borb来识别所有这些。
    我会做什么(在伪代码中):
    1.扩展SimpleTextExtraction(这是borb中处理从PDF提取文本的主类)
    1.每当这个类看到一个事件时(通常是解析器完成了一个特定的指令),你可以检查你看到的是一个文本呈现指令,还是一个线条绘制指令。跟踪文本,跟踪线条(特别是它们的边界框)。
    1.处理完页面上的所有事件后,从页面中获取所有注解,并过滤掉带删除线的注解。跟踪它们的边框。
    1.从TextRenderEvent对象列表中,过滤出边界框与以下对象重叠的对象:线条或带删除线的边界框
    1.复制用于从这些事件重建文本的基本算法
vwkv1x7d

vwkv1x7d2#

如果这些删除线实际上是注解,PyMuPDF提供了一个简单而快速的解决方案:在页面上列出所有带删除线的标注矩形,并提取它们“下面”的文本。或者,类似地,查看你感兴趣的关键词(如“男性”、“女性”),看看是否有被删除线覆盖的标注。

# strike out annotation rectangles
st_rects = [a.rect for a in page.annots(types=[fitz.PDF_ANNOT_STRIKE_OUT])]
words = page.get_text("words")  # the words on the page
for rect in st_rects:
    for w in words:
        wrect = fitz.Rect(w[:4])  # rect of the word
        wtext = w[4]  # word text
        if wrect.intersects(rect):
            print(f"{wtext} is strike out")

# the above checks if a word area intersects a strike out rect
# B/O mostly sloppy strike out rectangle definitions the safest way.
# alternatively, simpler:

for rect in st_rects:
    print(page.get_textbox(rect + (-5, -5, 5, 5)), "is striked out")

# here I have increased the strike out rect by 5 points in every direction
# in the hope to cover the respective text.

另一种情况是PDF绘图,即所谓的“艺术线条”。这些没有注解(可以删除),而是像直线、曲线、矩形这样的东西-永久存储在页面的渲染代码对象(/Contents)中。
PyMuPDF也允许你提取艺术线条。如果你的文本用这种方法被删除,那么在文本矩形和艺术线条矩形之间存在重叠。
Office软件(MS Word、LibreOffice)通常使用细矩形而不是实线来更好地科普缩放显示--因此,要捕捉所有这些情况,您必须同时选择水平线绝对高度小、宽度也大得多的矩形。
下面是提取这些水平线和“伪线”以及页面的代码:

lines = []  # to be filled with horizontal "lines": thin rectangles
paths = page.get_drawings()  # list of drawing dictionary objects
for path in paths:  # dictionary with single draw commands
    for item in path["items"]:  # check item types
        if item[0] in ("c", "qu"):  # skip curves and quads
            continue
        if item[0] == "l":  # a true line
            p1, p2 = item[1:]  # start / stop points
            if p1.y != p2.y:  # skip non-horizontal lines
                continue
            # make a thin rectangle of height 2
            rect = fitz.Rect(p1.x, p1.y - 1, p2.x, p2.y + 1)
            lines.append(rect)
        elif item[0] == "re":  # a rectangle, check if roughly a horizontal line
            rect = item[1]  # the item's rectangle
            if rect.width <= 2 * rect.height or rect.height > 4:
                continue  # not a pseudo-line
            lines.append(rect)

现在,您可以使用这些线条矩形来检查与文本矩形的任何交点。

相关问题