如何从这个压缩的 PDF/A 中提取文本?

How extract text from this compressed PDF/A?

出于机器学习目的(sckit-learn), I need to extract the raw text from lots of PDF files. First off, I was using xpdf pdftotext 完成此任务:

exe = r'"'+os.path.join(xpdf_path,"pdftotext.exe")+'"'
cmd = exe+" "+"\""+pdf+"\""+" "+"\""+pdf+".txt"+"\""
subprocess.check_output(cmd)
with open(pdf+".txt") as f:
    texto_converted = f.read()

但不幸的是,对于他们中的少数人,我无法获得文本,因为他们在他们的 pdf 源上使用 "stream",例如 this one.

结果是这样的:

59!"#$%&'()*+,-.#/#01"21"" 345667.0*(879:4$;<;4=<6>4?$@"12!/ 21#$@AA$>@>BCDCEFGCHIJKIJLMNIJILOCNPQRDS QPFTRPUCTCVQWBCTTQXFPYTO"21 "#/!"#(Z[12\&A+],^_3;9`Z &a# .2"#.b#"(#c#A(87*95d$d4?$d3e#Z"f#\"#2b?2"#`Z 2"!eb2"#H1TBRgF JhiO
jFK# 2"k#`Z !#212##"elf/e21m#*c!n2!!#/bZ!#2#`Z "eo ]<$@;A533> "/\ko/f\#e#e#p

我什至尝试使用 zlib + regex:

import re
import zlib

pdf = open("pdfa.pdf", "rb").read()
stream = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S)

for s in re.findall(stream,pdf):
    s = s.strip(b'\r\n')
    try:
        print(zlib.decompress(s).decode('UTF-8'))
        print("")
    except:
        pass

结果是这样的:

1 0 -10 -10 10 10 d1
0.01 0 0 0.01 0 0 cm
1 0 -10 -10 10 10 d1
0.01 0 0 0.01 0 0 cm

我什至试过 pdftopng (xpdf) 之后尝试 tesseract,但没有成功 那么,有没有办法像使用 Python 或第三方应用程序那样从 PDF 中提取纯文本?

您可以使用两种相当简单的技术。

1) Google 的 "Tessaract" 开源 OCR(光学字符识别)。您可以将此均匀地应用到所有 PDF,尽管将所有数据转换为像素然后对它们施展魔法将在计算上更加昂贵。哪个更重要,工程师时间还是 CPU 时间?有一个 pytesseract 模块。请注意,此工具适用于图像格式,因此您必须使用类似 GhostScript(另一个开源项目)的工具将 PDF 的所有页面转换为图像,然后 运行 [py]tessaract 处理这些图像。

2) pyPDF 可以获取每个页面并以编程方式提取任何文本绘制操作 按照它们在页面上绘制的顺序 。这可能与页面的逻辑阅读顺序完全不同......虽然 PDF 可以 绘制所有的 'a',然后绘制所有的 'b'(等等),但实际上效率更高在 "font a" 中绘制所有内容,然后在 "font b" 中绘制所有内容。重要的是要注意 "font b" 可能只是 "font a" 的斜体版本。这会产生一个 shorter/more 高效的绘图命令流,尽管数量可能不足以成为一个好的商业决策。

这里的关键是随机的一堆 PDF 文件可能需要您进行一些 OCR。组装不当的 PDF(其字体子集没有 "to unicode" 数据)无法正确挖掘文本,即使它只有文本绘制操作。如果您不知道前五个字形是 "g-l-y-p-h","Draw glyphs one through five from "font C" 意义不大,因为这是它们的使用顺序。

另一方面,如果您有自己制作的 PDF 或所有 pdf 都来自某个已知来源(例如 Word 的 pdf 转换器),您会提前知道会发生什么。

请注意,上面提到的唯一我实际使用过的是 Ghostscript。我记得它有一个可靠的命令行界面,我们在很多年前为一些在线 PDF 查看器生成图像。

如果你想解压缩 PDF 文件中的流,我可以推荐使用 qdpf,但在这个文件上

 qpdf --decrypt --stream-data=uncompress document.pdf out.pdf

也无济于事。

我不确定为什么你在 xpdftesseract 上的努力没有成功,使用 image-magick 的 convert 要在临时目录和 tesseract 中创建 PNG 文件,您可以这样做:

import os
from pathlib import Path
from tempfile import TemporaryDirectory
import subprocess

DPI=600

def call(*args):
    cmd = [str(x) for x in args]
    return subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('utf-8')

def ocr(docpath, lang):
    result = []
    abs_path = Path(docpath).expanduser().resolve()
    old_dir = os.getcwd()
    out = Path('out.txt')
    with TemporaryDirectory() as tmpdir:
         os.chdir(tmpdir)
         call('convert', '-density', DPI, abs_path, 'out.png')
         index = -1
         while True:
             # names have no leading zeros on the digits, would be difficult to sort glob() output
             # so just count them
             index += 1
             png = Path(f'out-{index}.png')
             if not png.exists():
                 break
             call('tesseract', '--dpi', DPI, png, out.stem, '-l', lang)
             result.append(out.read_text())
         os.chdir(old_dir)
    return result

pages = ocr('~/Downloads/document.pdf', 'por')
print('\n'.join(pages[1].splitlines()[21:24]))

给出:

DA NÃO REALIZAÇÃO DE AUDIÊNCIA DE AUTOCOMPOSIÇÃO NO CASO EM CONCRETO

Com vista a obter maior celeridade processual, assim como da impossibilidade de conciliação entre

如果您使用 Windows,请确保您的 PDF 文件未在其他进程(如 PDF 查看器)中打开,因为 Windows 似乎不喜欢那样。

最终的print由于完整的输出量很大

有限。

此转换和 OCR-ing 需要一段时间,因此您可能需要取消注释 call() 中的 print 以获得一些进展。