按内容查找重复的PDF文件


9

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

或者有纸质版本带有超级链接,而文本版本带有文本引用。

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

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

我也在compareImageMagick套件中进行了尝试,但是使用此工具无法处理多页PDF文件。

diffpdf 2.1.1在两个文件的GUI中做得很好,但我不知道如何将其应用于许多文件,并且在任何开放源代码许可下都无法使用最新版本。


1
由于答案之间有非常不同的方法,因此最好更加具体并阐明问题。现在,您是在寻找一种健壮的方法来比较不同的pdf文件,包括科学论文,还是在寻找一种高效,优雅的解决方案来比较期刊文章,仅检查标题或DOI是否匹配就足够了。
inVader 2015年

我正在寻找类似的解决方案-现在我使用的是md5,当每次下载都在pdf中记录时间和ip时出现问题。我正在使用imagemagick设计一个带有包装程序脚本的解决方案,以循环浏览页面(并可能尝试跳过第一页,以防它是日记添加的标题)。我非常有信心这是最强大的解决方案。您知道它会很好地工作,因为它是一个人在视觉上比较两个文档时使用的相同方法。它也完全独立于文档的生成方式,仅依赖其外观。
Orion

我还要说,单页比较可能就足够了-如果一页相同,则两个文档不太可能会不同。该符号blah.pdf[1]将调用文档中的所需页面。
Orion

如果您真的需要比较其中一个或两个都是基于扫描的pdf文件,我认为您无法避免使用OCR。因此,此处建议的许多方法并不能真正解决问题。
gogoud

Answers:


4

由于不同的出版商使用不同的“标记” 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用来提取文本,过滤掉标记(如果过滤得很少,这没问题),然后根据那。

另外,您可以比较通过散列找到的旧文件的文件大小,并查看新文件是否在一定范围内。字符串中的压缩和ifferences(IP /日期时间标记)应仅导致小于百分之一的差异。

如果您知道发布者确定哈希值时使用的方法,则可以直接应用上述方法中的“正确”方法,但是即使没有这种方法,您也可以检查元数据并应用启发式方法,或者确定文件中的图像数量并将其与页数进行比较(如果关闭的话,您可能拥有包含扫描件的文档)。pdftext扫描图像上的PDF也具有可识别的输出。


作为工作的基础,我创建了一个位于bitbucket上的python程序包,并且/或者可以使用PyPI安装该程序包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。


符合IP的stringparts可以替换为Python的re模块:

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'

过去,我也使用python包pdfrw提取元数据,但无法在任何地方处理加密的pdf文件pdfinfo
Anthon

2

我会给pdftotext另一个机会,至少对于您集合中实际上有文本的PDF(否则,您将需要运行OCR),请使用更好的工具来处理输出。

输出(肮脏的)文本输出后,通过旨在确定相似性的程序运行(而不是diff逐行差异,这将是解决精神错乱的捷径)。

考虑类似perl的String :: Similaritysimhash程序(在Debian中可用,但在Fedora / RHEL中不可用)。


2

PDF包含元数据,我刚刚检查了许多来自不同出版商的与物理学相关的论文,而且它们都至少具有“标题”属性。对于某些标题,标题是出版物的实际标题,对于某些标题,标题包含DOI或类似的标识符。无论如何,我检查的每篇论文都包含标题,对于给定的出版物而言,它始终是唯一的。

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

要将所有元数据转储到文本文件(或stdout)中以进行进一步处理

pdftk <PDF> dump_data output <TEXTFILE>

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

如果您想尝试ImageMagick的方法,compare但是有多个页面会导致问题,您还pdftk可以提取单个页面并分别比较所有页面(尽管比较一个页面就足够了)。

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


1

您是否看过PDF Content Comparer?有命令行选项可以使您自动执行该过程。

您可以在它创建的差异日志上运行某种逻辑,以查看它们之间的相似程度。

否则,您可能会尝试将PDF临时拆分为多个文件,然后以这种方式进行比较。但是,您可能仍会以这种方式重复。一个PDF可能只包含一个额外的空白页,或可能导致后续所有页进行比较的完全不同的空白。


可能是此封闭源程序的两个最昂贵的版本可以完成任务。我希望有一个开源解决方案,尽管它不需要免费。
乔纳斯·斯坦

1

在对讨论的不起眼的贡献(部分答案)之后:

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

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


1

我有一个查看pdf的脚本,然后首先尝试使用提取文本pdftotext,但是如果失败(如对扫描的文档会失败),它将使用ghostscript多页扫描的pdf转换为一系列png文件,然后使用tesseract将此系列转换为单个文本文件。如果扫描的质量足够好,则可以做得很好。添加用于比较文件之间文本的代码很简单,但是我没有这个要求。

ghostscript和tesseract都是开源的,可以从命令行使用。


您可以使用pdfimagespoppler软件包直接提取扫描的图像,而不会因通过ghostscript渲染而获得质量的额外损失(这会对您要执行的任何OCR产生负面影响)。
Anthon

@Anthon感谢您指出这一点,但肯定pdfimagesgs这里的ghostscript()一样,即将图像从pdf提取为jpg / png。为什么比这更好gs呢?
gogoud

除非所有扫描具有相同的分辨率(否则,例如,如果放弃空白边缘,则除非所有扫描具有相同的分辨率),然后只有当您以图像使用的分辨率完全相同时,ghostscript的渲染才会扭曲图像的像素
Anthon

@Anthon有趣的是,我已经做了一些测试。结果非常相似,但是gs/ tesseract(png中间格式)的效果似乎好于pdfimages/ tesseract(pbm中间格式)。pdfimages虽然更快。
gogoud

0

我将提供perl作为解决方案。有一个名为的模块CAM::PDF,可让您提取PDF内容。

它的工作原理如下:

#!/usr/bin/perl

use strict;
use warnings;

use CAM::PDF;

my $file = 'sample.pdf';

my $pdf = CAM::PDF->new($file);

my $word_count = 0;
for my $pagenum ( 1 .. $pdf->numPages ) {
    my $page_text = $pdf->getPageText($pagenum) );
    print $page_text; 
}

您可以提取文本并进行比较。

对于仅扫描的文档-这要困难得多,但是假设它们使用的是相同的基本图像(例如,没有单独扫描它们),则可以使用:

#!/usr/bin/perl

use strict;
use warnings;

use CAM::PDF;
use CAM::PDF::Renderer::Images;
use Data::Dumper; 

my $file = 'sample.pdf';

my $pdf = CAM::PDF->new($file);

my $word_count = 0;
for my $pagenum ( 1 .. $pdf->numPages ) {
    my $content =  $pdf->getPageText($pagenum);
    my $page = $pdf->getPageContentTree($pagenum);
    my $gs = $page->findImages();
    my @imageNodes = @{$gs->{images}};
    print Dumper \@imageNodes;

    print Dumper \$gs;
}

我没有对其进行特别良好的测试,因为我没有您的原始文档。我认为这种方法应该可以解决问题-您没有在比较实际的图像内容,因为..嗯,那确实很困难。但是您应该能够从元数据中识别出类似的图像。

对于具有不同元数据的相同 PDF,那么一些简单的事情就可以解决,例如对文本内容和图像元数据进行哈希处理。


-1

有一个Linux应用程序,称为recoll。它可以执行任务,但仅适用于带有文本层的pdf。


2
对我来说recoll似乎是一个桌面搜索引擎。我看不到,如何使用它来查找重复项。
乔纳斯·斯坦

1
recoll用于pdftotext处理PDF,这是OP在此试图避免的事情。
约翰·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.