按内容查找重复的 PDF 文件

按内容查找重复的 PDF 文件

有些期刊每次下载都会生成不同的 PDF。以APS为例在 PDF 中存储时间和 IP 地址。

或者有一个带有超链接的纸质版本和一个带有文本参考的纸质版本。

如何使用开源软件在Linux系统上找到内容90%相等的论文的重复下载?

我一直在考虑将 PDF 文件转换为临时目录中的纯文本pdf2txt。然后我可以过滤所有diff a b结果超过 x 行的文件名。但这一点也不优雅,并且对于扫描的出版物来说会失败。期刊通常不提供旧出版物的 OCR 文本。

我也尝试过compare使用 ImageMagick 套件,但无法使用该工具处理多页 PDF 文件。

差异PDF 2.1.1在两个文件上的 GUI 中做得很好,但我不知道如何将它应用到许多文件上,并且最新版本在任何开源许可证下都不可用。

答案1

由于不同的出版商使用不同的方法来“标记” PDF,因此您需要确保在进行比较时不考虑标记。

您还需要一种有效的方法来将新的 PDF 与所有已下载的 PDF 进行比较,以防您重复下载相同的 PDF,并且它如您建议的那样标记有 IP 和/或日期时间戳。您不想使用耗时的比较机制来将每个新 PDF 与许多已下载的 PDF 进行比较

您需要的是一个实用程序,它可以剥离每个可能的标记并生成剩余数据的哈希值。您将需要保留一个散列→文件名映射,它可以在一个简单的文件中,如果计算出的散列已经在文件中,则您有一个副本(并删除它或执行任何需要的操作),如果散列尚未存在在那里,您添加哈希值和文件名。该文件看起来像:

6fcb6969835d2db7742e81267437c432  /home/anthon/Downloads/explanation.pdf
fa24fed8ca824976673a51803934d6b9  /home/anthon/orders/your_order_20150320.pdf

与原始 PDF 相比,该文件小得可以忽略不计。如果您有数百万个 PDF,您可能会考虑将这些数据存储在数据库中。为了提高效率,您可能需要在其中包含文件大小和页数 ( pdfinfo | egrep -E '^Pages:' | grep -Eo '[0-9]*')。


以上将问题推向了删除标记并生成哈希。如果您在调用哈希生成例程时知道 PDF 来自何处(即,如果您以编程方式进行下载),则可以基于此微调哈希生成。但即使没有,哈希生成也有几种可能性:

  1. 如果标题和作者的元数据非空且不包含“Acrobat”或“PDF”等非特定字符串,您可以仅根据作者和标题信息生成哈希值。用于pdfinfo -E file.pdf | grep -E '^(Author:)|(Title:) | md5sum获取哈希值。您也可以在计算哈希时包含页数(输出Pages:中的“ ” pdfinfo)。
  2. 如果前面的规则不起作用并且 PDF 包含图像,则提取图像并在组合图像数据上生成哈希。如果图像在页脚或页眉中包含文本(例如“许可给 Joe 用户”),请在计算哈希值之前从顶部或底部剥离 X 行。如果该标记位于一些大字母灰色背景文本中,这当然不起作用,除非您过滤掉不完全黑色的像素(为此您可以使用imagemagick)。您可以使用pdfimages将图像信息提取到临时文件中。
  3. 如果前面的规则不起作用(因为没有图像),您可以用来pdftext提取文本,过滤掉标记(如果过滤掉太多,那不是问题),然后根据那。

此外,您可以比较通过哈希找到的旧文件的文件大小是否与新文件在一定范围内。字符串(IP/日期时间戳)中的压缩和差异应该只会导致小于 1% 的差异。

如果您知道发布者在确定哈希值时使用的方法,则可以直接应用上述“正确”方法,但即使没有,您也可以检查元数据并应用一些启发式方法,或者确定文件中的图像数量并将其与页数进行比较(如果它们很接近,您可能有一个由扫描件组成的文档)。pdftext在扫描图像 PDF 上也有可识别的输出。


作为工作的基础,我创建了一个 python 包,位于位桶和/或可以从安装吡啶甲酸使用pip install ruamel.pdfdouble。这为您提供了pdfdbl对元数据、提取的图像或文本执行上述扫描的命令。 它不做任何过滤标记(然而),但自述文件描述了要增强哪(两种)方法来添加该内容。

包含的自述文件:

ruamel.pdfdouble

这个包提供了pdfdbl命令:

pdfdbl scan dir1 dir2

这将遍历作为参数提供的目录,并为找到的 PDF 文件创建基于(按顺序)的哈希值:

  • 元数据(如果唯一)
  • 图像 如果图像数量
  • 文本

假设 poppler-utils 包中的 pdfinfo、pdfimages 和 pdftotext` 可用。

建立一个“数据库”,~/.config/pdfdbl/pdf.lst用于测试进一步的扫描。

去除标记

ruamel/pdfdouble/pdfdouble.py两种方法可以增强过滤掉 PDF 中的标记,这些标记会降低它们的唯一性,并使几乎相同的文件具有不同的哈希值。

对于文本,应扩展该方法PdfData.filter_for_marking以从作为其参数的字符串中删除标记并返回结果。

PdfData.process_image_and_update对于扫描图像,需要增强该方法,例如,通过剪掉图像底部和顶部的 X 线,以及通过将所有黑色像素设置为白色来删除任何灰色背景文本。该函数需要使用.update()传入过滤数据的方法来更新传入的哈希值。

限制

当前“数据库”无法处理包含换行符的路径

该实用程序目前仅适用于 Python 2.7。


re符合 IP 的字符串部分可以用 Python 的模块替换:

import re
IPre = re.compile("(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}"
              "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")

x = IPre.sub(' ', 'abcd 132.234.0.2 ghi')
assert x == 'abcd   ghi'

答案2

我会再给你pdftotext一次机会,至少对于你收藏中实际上有文本的 PDF(否则你需要运行 OCR),使用更好的工具来处理输出。

一旦获得(脏)文本输出,就通过一个旨在确定相似性的程序运行它(而不是diff逐行差异,这将是导致疯狂的快速路径)。

考虑类似 perl 的东西字符串::相似性或者西姆哈什程序(在 Debian 中可用,但在 Fedora/RHEL 中不可用)。

答案3

PDF 包含元数据,我刚刚检查了来自不同出版商的一些物理相关论文,它们都至少具有“标题”属性。对于某些人来说,标题是出版物的实际标题,对于某些人来说,它包含 DOI 或类似的标识符。不管怎样,我检查的每篇论文都包含标题,而且它对于给定的出版物来说总是独一无二的。

您可以用来pdftk访问 PDF 的元数据并进行比较。就您的目的而言,这绝对应该足够了,并且pdftotext比性能成为问题时要快得多。如果一篇论文确实不应该有标题元数据,您仍然可以退回到pdftotext.

将所有元数据转储到文本文件(或标准输出)以供进一步处理使用

pdftk <PDF> dump_data output <TEXTFILE>

或参阅手册了解更多选项。

如果你想尝试图像魔术师compare多个页面会导致问题,您也可以使用pdftk提取单个页面并分别比较所有页面(不过,也许只比较单个页面就足够了)。

diff以下是使用此方法为多页 PDF 创建类似 PDF 输出的代码片段:https://gist.github.com/mpg/3894692

答案4

以下是对讨论的谦虚贡献(部分答案):

转换为文本后,我将使用以下内容来计算(基于单词差异的)文件相似度:

wdiff -s -123 file1.txt file2.txt |    ## word difference statistics (1)
     grep -Po '(\d+)(?=% common)' |    ## 
     awk '{a+=$1}END{print a/2}'       ## (2)

(1) 产生如下结果

file1.txt: 36 words  33 92% common  3 8% deleted  0 0% changed
file2.txt: 35 words  33 94% common  2 6% inserted  0 0% changed

(2) = 93

相关内容