Trova file PDF duplicati per contenuto


9

Alcune riviste generano un PDF diverso per ogni download. APS, ad esempio, memorizza l'ora e l'indirizzo IP nel PDF.

Oppure esiste una versione cartacea con collegamenti ipertestuali e una con riferimenti testuali.

Come è possibile trovare download duplicati di documenti con uguale contenuto al 90% su un sistema linux usando un software open source?

Ho pensato di convertire i file PDF in testo semplice in una directory temporanea con pdf2txt. Quindi potrei filtrare tutti i nomi di file che diff a brisultano più di x righe. Ma questo non è affatto elegante e fallirà con le pubblicazioni scansionate. Le riviste spesso non forniscono testo OCR per vecchie pubblicazioni.

Ho anche provato comparenella suite ImageMagick, ma non sono riuscito a gestire file PDF multipagina con questo strumento.

diffpdf 2.1.1 fa un buon lavoro in una GUI su due file, ma non sono riuscito a capire come applicarlo su molti file e le versioni recenti non sono disponibili con alcuna licenza open source.


1
Poiché ci sono approcci molto diversi tra le risposte, potrebbe essere utile essere più specifici e chiarire la domanda. Stai ora cercando un modo affidabile per confrontare diversi file pdf tra cui documenti scientifici tra gli altri o stai cercando di trovare una soluzione efficiente ed elegante per confrontare articoli di riviste, in cui è sufficiente verificare se il titolo o DOI sono corrispondenti.
inVader,

Sto cercando una soluzione simile - ora sto usando md5 che è problematico quando ogni download registra tempo e ip nel pdf. Sto lavorando a una soluzione con imagemagick con uno script wrapper per scorrere ciclicamente le pagine (e possibilmente provare a saltare la prima pagina nel caso in cui sia l'intestazione aggiunta dal diario). Sono fiducioso che questa sia la soluzione più solida possibile. Sai che funzionerà molto bene perché è lo stesso metodo che una persona usa quando confronta visivamente due documenti. È anche completamente indipendente dal modo in cui il documento viene generato, solo dal suo aspetto visivo.
Orione,

Direi anche che un confronto di una singola pagina è probabilmente sufficiente: è improbabile che due documenti siano diversi se una pagina è uguale. La notazione blah.pdf[1]chiamerà una pagina desiderata dal documento.
Orione,

Se hai davvero bisogno di confrontare i pdf in cui uno o entrambi sono basati sulla scansione, penso che non puoi evitare di usare l'OCR. Molti degli approcci suggeriti qui quindi non risolvono realmente il problema.
gogoud,

Risposte:


4

Poiché diversi editori utilizzano metodi diversi per "contrassegnare" i PDF, è necessario assicurarsi di effettuare un confronto senza tenere conto dei contrassegni.

È inoltre necessario un metodo efficace per confrontare un nuovo PDF con tutti i PDF già scaricati nel caso in cui si scarichi ripetutamente lo stesso PDF ed è ad esempio contrassegnato con l'IP e / o la data e l'ora come si suggerisce. Non vuoi usare un meccanismo di confronto che richiede tempo che confronta ogni nuovo PDF con molti PDF già scaricati

Ciò di cui hai bisogno è un'utilità che elimini ciascuno dei possibili contrassegni e generi un hash dei dati rimanenti. Dovrai mantenere un hash → mappa del nome del file, che può essere in un semplice file, e se un hash calcolato è già nel file hai un duplicato (ed eliminalo o fai tutto il necessario) e se l'hash non è ancora lì, aggiungi l'hash e il nome del file. Il file sarebbe simile a:

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

Quel file è negligentemente piccolo rispetto ai PDF originali. Se hai milioni di PDF potresti prendere in considerazione la possibilità di archiviare questi dati in un database. Per motivi di efficienza, potresti voler includere la dimensione del file e il numero di pagine ( pdfinfo | egrep -E '^Pages:' | grep -Eo '[0-9]*').


Quanto sopra spinge il problema a rimuovere i segni e generare l'hash. Se sai da dove proviene il PDF quando invochi la routine di generazione dell'hash (ovvero se esegui i download a livello di codice), puoi ottimizzare la generazione dell'hash in base a quello. Ma anche senza questo ci sono diverse possibilità per la generazione di hash:

  1. se i metadati per titolo e autore non sono vuoti e non includono stringhe non specifiche come "Acrobat" o "PDF", è possibile generare l'hash in base solo alle informazioni sull'autore e sul titolo. Usa pdfinfo -E file.pdf | grep -E '^(Author:)|(Title:) | md5sumper ottenere l'hash. Puoi anche calcolare il numero di pagine nel calcolo dell'hash (' Pages:' pdfinfonell'output).
  2. se la regola precedente non funziona e il PDF contiene immagini, estrarre le immagini e generare un hash sui dati di immagine combinati. Se le immagini contengono mai testo nel piè di pagina o nell'intestazione come "Concesso in licenza a Joe User", rimuovere un numero X di righe dalla parte superiore o inferiore, prima di calcolare l'hash. Se quei segni sono in un testo di sfondo grigio con lettere grandi, questo ovviamente non funzionerà, a meno che tu non filtri i pixel che non sono totalmente neri (per questo potresti usare imagemagick). È possibile utilizzare pdfimagesper estrarre le informazioni sull'immagine in un file temporaneo.
  3. se le regole precedenti non funzionano (perché non ci sono immagini) è possibile utilizzare pdftextper estrarre il testo, filtrare il segno (se si filtra un po 'troppo, non è un problema) e quindi generare l'hash in base a quello.

Inoltre, puoi confrontare se la dimensione del file precedente è stata trovata tramite l'hash e vedere se rientra in determinati margini con il nuovo file. Compressione e ifferenze nelle stringhe (IP / data-ora-timbro) dovrebbero comportare solo una differenza inferiore all'uno percento.

Se conosci il metodo utilizzato dall'editore per determinare l'hash, puoi applicare direttamente il metodo "giusto" di cui sopra, ma anche senza di ciò puoi controllare i metadati e applicare alcune euristiche o determinare il numero di immagini in un file e confrontalo con il numero di pagine (se sono vicine probabilmente hai un documento composto da scansioni). pdftextsui PDF di immagini scansionate ha anche un output riconoscibile.


Come base per lavorare da ho creato un pacchetto python che è su bitbucket e / o che può essere installato da PyPI usando pip install ruamel.pdfdouble. Ciò fornisce il pdfdblcomando che esegue la scansione come descritto sopra su metadati, immagini estratte o testo. Non esegue alcun filtraggio dei contrassegni (ancora) , ma il readme descrive quali (due) metodi per migliorare per aggiungerlo.

Il readme incluso:

ruamel.pdfdouble

questo pacchetto fornisce il pdfdblcomando:

pdfdbl scan dir1 dir2

In questo modo verranno visualizzate le directory fornite come argomento e, per i file PDF trovati, verrà creato un hash basato su (in ordine):

  • metadati se unici
  • immagini se il numero di immagini
  • testo

Ciò presuppone che pdfinfo, pdfimages e pdftotext` dal pacchetto poppler-utils siano disponibili.

Viene creato un "database" in ~/.config/pdfdbl/pdf.lstbase al quale vengono testate ulteriori scansioni.

Rimozione dei segni

Nel ruamel/pdfdouble/pdfdouble.pyci sono due metodi che possono essere migliorato per filtrare le marcature nel PDF che li rendono meno unici e rendere praticamente gli stessi file per avere diverse hash.

Per il testo, il metodo PdfData.filter_for_markingdeve essere esteso per rimuovere e contrassegnare dalla stringa che è i suoi argomenti e restituire il risultato.

Per le immagini scansionate il metodo PdfData.process_image_and_updatedeve essere migliorato, ad esempio tagliando le linee X in basso e in alto e rimuovendo qualsiasi testo di sfondo grigio impostando tutti i pixel neri su bianco. Questa funzione deve aggiornare l'hash passato usando il .update()metodo che passa nei dati filtrati.

restrizioni

L'attuale "database" non è in grado di gestire percorsi che contengono newline

Questa utility è attualmente solo Python 2.7.


Le stringhe conformi IP possono essere sostituite con il remodulo 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'

In passato ho usato il pacchetto python anche pdfrwper estrarre metadati, ma che non è in grado di gestire file pdf crittografati, dove pdfinfopossibile.
Anthon,

2

Darei pdftotextun'altra possibilità, almeno per i PDF nella tua raccolta che contengono effettivamente testo (altrimenti avresti bisogno di eseguire l'OCR), usando uno strumento migliore per elaborare l'output.

Una volta che hai il tuo (sporco) output di testo, eseguilo attraverso un programma progettato per determinare le somiglianze (piuttosto che diffle differenze riga per riga, che sarebbe un rapido percorso verso la follia).

Considera qualcosa come String :: Similarity del perl o il programma simhash (che è disponibile in Debian ma non in Fedora / RHEL).


2

I PDF contengono metadati e ho appena controllato una serie di articoli relativi alla fisica di diversi editori e tutti hanno almeno l'attributo "Titolo". Per alcuni, il titolo è l'effettivo titolo della pubblicazione, per alcuni contiene il DOI o identificatori simili. Ad ogni modo, ogni documento che ho controllato contiene il titolo ed è sempre qualcosa di unico per la pubblicazione data.

È possibile utilizzare pdftkper accedere ai metadati dei PDF e confrontarli. Per il tuo scopo, questo dovrebbe essere sicuramente sufficiente ed è molto più veloce che pdftotextse le prestazioni sono un problema. Nel caso in cui un articolo in realtà non dovrebbe avere metadati del titolo a cui potresti ancora ricorrere pdftotext.

Per scaricare tutti i metadati in un file di testo (o stdout) per ulteriore elaborazione, utilizzare

pdftk <PDF> dump_data output <TEXTFILE>

oppure consultare il manuale per ulteriori opzioni.

Se volete provare ImageMagick 's compare, ma le pagine multiple causa un problema, si potrebbe anche usare pdftkper estrarre singole pagine e confrontare tutti loro separatamente (forse solo paragonando uno solo è sufficiente, però).

Ecco uno snippet di codice che utilizza questo approccio per creare un diffoutput PDF simile a PDF multipagina: https://gist.github.com/mpg/3894692


1

Hai guardato in PDF Content Comparer ? Esistono opzioni da riga di comando che dovrebbero consentire di automatizzare il processo.

È possibile eseguire una sorta di logica sul registro delle differenze che crea per vedere quanto sono simili.

In caso contrario, potresti provare a dividere temporaneamente i PDF in più file e confrontarli in quel modo. Probabilmente avresti comunque dei duplicati in quel modo. Un PDF potrebbe avere solo una pagina vuota in più o qualcosa che farebbe confrontare tutte le pagine successive come completamente diverse.


Possono essere le due versioni più costose di questo programma chiuso che può fare il lavoro. Preferirei una soluzione open source, anche se non è necessario che sia gratuita.
Jonas Stein,

1

A seguito di un modesto contributo alla discussione (risposta parziale):

Dopo la conversione in testo, per calcolare la smilarità del file (in base alla differenza di parole) utilizzerei quanto segue:

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

(1) produce un risultato simile

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

Ho uno script che guarda un pdf e cerca prima di estrarre il testo usando pdftotext, ma se questo fallisce (come farà con un documento scansionato), usa ghostscript per trasformare un pdf scansionato su più pagine in una serie di file png e poi usa tesseract per convertire questa serie in un singolo file di testo. Se la scansione è di qualità sufficiente, fa un ottimo lavoro. Sarebbe semplice aggiungere codice confrontando il testo tra i file ma non ho avuto questo requisito.

ghostscript e tesseract sono entrambi open source e funzionano dalla riga di comando.


Puoi estrarre direttamente le immagini scansionate usando pdfimagesdal pacchetto poppler senza ulteriore perdita di qualità che potresti ottenere con il rendering tramite ghostscript (che influenza negativamente qualsiasi OCR che vuoi fare).
Anthon,

@Anthon grazie per averlo sottolineato, ma sicuramente pdfimagessta facendo lo stesso di ghostscript ( gs) qui, ovvero estraendo immagini da pdf a jpg / png. Perché è meglio di così gs?
gogoud,

Il rendering di ghostscript distorce i pixel delle immagini a meno che tutte le scansioni non abbiano la stessa risoluzione (non nel caso, ad esempio, se i bordi degli spazi siano stati scartati) e solo se si esegue il rendering esattamente alla stessa risoluzione utilizzata dalle immagini
Anthon

@Anthon Interessante, ho fatto un piccolo test. I risultati sono molto simili ma sembra che gs/ tesseract(formato intermedio png) funzioni leggermente meglio di pdfimages/ tesseract(formato intermedio pbm). pdfimagesè più veloce però.
gogoud,

0

Vorrei offrire perl come soluzione. C'è un modulo chiamato CAM::PDFche ti consente di estrarre ... contenuto PDF.

Funziona un po 'così:

#!/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; 
}

Puoi estrarre il testo e confrontarlo.

Per i soli documenti scansionati: è molto più difficile, ma supponendo che stiano usando le stesse immagini di base (ad es. Non le hanno scansionate separatamente), probabilmente puoi usare:

#!/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;
}

Non l'ho testato particolarmente bene, perché non ho i tuoi documenti di origine. Penso che questo approccio dovrebbe fare il trucco - non stai confrontando il contenuto dell'immagine reale, perché ... beh, è ​​davvero difficile. Ma dovresti essere in grado di riconoscere immagini simili dai metadati.

Per PDF identici con metadati diversi, qualcosa di semplice come l'hashing del contenuto del testo e dei metadati dell'immagine dovrebbe fare il trucco.


-1

Esiste un'applicazione Linux, chiamata recoll . Può eseguire l'attività, ma solo per i PDF con livello di testo.


2
A me recollsembra essere un motore di ricerca desktop. Non riuscivo a vedere come usarlo per trovare duplicati.
Jonas Stein,

1
recollutilizza pdftotextper gestire i PDF, che è ciò che l'OP sta cercando di evitare qui.
John WH Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.