从损坏的PDF文档中提取文本

3
在我正在处理的项目中,我们从各个政府网站爬取法律文件并使其在线可搜索。不时会遇到似乎损坏的PDF文件。这里是一个例子。如果你在PDF阅读器中打开它,看起来没问题,但是:如果你尝试复制和粘贴它,你会得到损坏的文本;如果你运行任何像pdftotext这样的工具,你也会得到损坏的文本;如果你对它做任何其他操作——你猜对了——你会得到损坏的文本。然而,如果你在阅读器中打开它,看起来就没问题!所以我知道文本在那里,但是出了些问题。结果是,在我的网站上它看起来非常糟糕。有什么办法可以解决吗? 更新:我今天进行了更多的研究。感谢@Andrew Cash的观察,发现这本质上是凯撒密码,我意识到我可以搜索这些文件。此链接将向您展示我系统中的大约200个这样的文件。浏览更大的样本集时,看起来这些都是由同一软件pdffactory v. 3.51创建的!所以我认为这是一个错误,而不是故意混淆。

更新2:上面的链接将不再提供任何结果。使用下面的解决方案从我的系统中清除了这些内容。


有许多方法可以“混淆”PDF文件,但如果它们在屏幕上正确呈现,您应该能够通过谷歌搜索“PDF OCR”来找到一个产品,只需呈现它们并将其转换回文本。另一种选择是使用例如Ghostscript将它们转换为图像,并使用几乎任何OCR软件。 - Joachim Isaksson
我建议使用OCR程序。这样,您就不会尝试阅读可能损坏的文档了。 - Cody Gray
2个回答

3

该PDF使用子集字体,其中字符被重新映射到其他字符,使用相同的简单的第二次世界大战替换密码。

A = G, B = 1, C =#, D = W, ......等等。每个字符都被重新映射。

字体是这样映射的,为了在PDF中显示正确的字符,您需要发送“G1#W”以便打印出ABCD。通常,PDF将具有ToUnicode表,以帮助您进行文本提取,但我怀疑出于某种目的故意省略了此表。

我自己看过一些这样的文件,它们被故意混淆以防止文本提取。我见过一个文档,其中约有5种不同的字体,并且它们都使用不同的序列进行映射。

确定是否存在此问题的一种方法是将PDF加载到Acrobat中,并将文本复制/粘贴到文本编辑器中。如果Acrobat无法将文本解码回英语,则没有办法在不手动重新映射的情况下提取文本,如果您知道翻译映射,则可以手动重新映射。

从这些类型的文档中轻松提取文本的唯一方法是OCR完整文档并删除原始文本。 OCR会将页面转换为TIFF图像,然后进行OCR,因此原始的混乱文本不应影响OCR。


没想到凯撒密码是这个的核心。不过看起来并不是故意的 - 请看我的更新。 - mlissner
1
哈哈哈...你对技术方面的解释相当正确(并且诚实地赞扬:对于PDF新手来说也很容易理解)。但让我微笑的是,你正在编造一个关于“故意混淆”的阴谋论,而实际上只是字体的“自定义编码”(有充分的文档记录),因为不想嵌入完整的字体,以考虑许可证或空间问题。;-) - Kurt Pfeifle

2

对于这个问题感到厌烦,不想处理OCR,我手动整理了密码。在此,附上一个Python字典以及一些基本的测试代码。我相信这可以改进,但它确实适用于除大写字母Q和大写字母X之外的所有字母,我还没有找到它们。

目前缺少相当多的标点符号(例如,所有这些都缺少:<>?{}\|!~`@#$%^_=+)。

# -*- coding: utf-8 -*-

import re
import sys

letter_map = {
 u'¿':'a',
 u'regex':'b',
 u'regex':'c',
 u'regex':'d',
 u'»':'e',
 u'o':'f',
 u'1':'g',
 u'regex':'h',
 u'·':'i',
 u'¶':'j',
 u'μ':'k',
 u'regex':'l',
 u'3':'m',
 u'2':'n',
 u'±':'o',
 u'°':'p',
 u'regex':'q',
 u'®':'r',
 u'-':'s',
 u'¬':'t',
 u'«':'u',
 u'a':'v',
 u'©':'w',
 u'regex':'x',
 u'§':'y',
 u'¦':'z',
 u'ß':'A',
 u'Þ':'B',
 u'Ý':'C',
 u'Ü':'D',
 u'Û':'E',
 u'Ú':'F',
 u'Ù':'G',
 u'Ø':'H',
 u'×':'I',
 u'Ö':'J',
 u'Õ':'K',
 u'Ô':'L',
 u'Ó':'M',
 u'Ò':'N',
 u'Ñ':'O',
 u'Ð':'P',
 u'':'Q', # Missing
 u'Î':'R',
 u'Í':'S',
 u'Ì':'T',
 u'Ë':'U',
 u'Ê':'V',
 u'É':'W',
 u'':'X', # Missing
 u'Ç':'Y',
 u'Æ':'Z',
 u'ð':'0',
 u'ï':'1',
 u'î':'2',
 u'í':'3',
 u'ì':'4',
 u'ë':'5',
 u'ê':'6',
 u'é':'7',
 u'è':'8',
 u'ç':'9',
 u'ò':'.',
 u'ô':',',
 u'æ':':',
 u'å':';',
 u'Ž':"'",
 u'•':"'",
 u'•':"'", # s/b double quote, but identical to single.
 u'Œ':"'", # s/b double quote, but identical to single.
 u'ó':'-', # dash
 u'Š':'-', # n-dash
 u'‰':'--', # em-dash
 u'ú':'&',
 u'ö':'*',
 u'ñ':'/',
 u'÷':')',
 u'ø':'(',
 u'Å':'[',
 u'Ã':']',
 u'‹':'•',
 }

ciphertext = u'''YOUR STUFF HERE'''

plaintext = ''

for letter in ciphertext:
    try:
        plaintext += letter_map[letter]
    except KeyError:
        plaintext += letter

# These are multi-length replacements
plaintext = re.sub(u'm⁄4', 'b', plaintext)
plaintext = re.sub(u'g⁄n', 'c', plaintext)
plaintext = re.sub(u'g⁄4', 'd', plaintext)
plaintext = re.sub(u' ́', 'l', plaintext)
plaintext = re.sub(u' ̧', 'h', plaintext)
plaintext = re.sub(u' ̈', 'x', plaintext)
plaintext = re.sub(u' ̄u', 'qu', plaintext)

for letter in plaintext:
    try:
        sys.stdout.write(letter)
    except UnicodeEncodeError:
        continue

你会发现这可能适用于特定的PDF,但在未来你会看到不同的编码方案,并且在某些文档中每个字体将使用不同的随机编码。 - Andrew Cash
我不知道,我用它处理了200个PDF文件,并进行了一些抽查。出于某种原因,似乎完美地运行了。 - mlissner
正如您所说,所有的问题 PDF 都来自同一生产者 - 很高兴听到这解决了您的问题。 - Andrew Cash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接