Encuentra archivos PDF duplicados por contenido

9

Algunas revistas generan un PDF diferente para cada descarga. APS, por ejemplo, almacena el tiempo y la dirección IP en el PDF.

O hay una versión en papel con hipervínculos y otra con referencias de texto.

¿Cómo es posible encontrar descargas duplicadas de documentos con un contenido igual al 90% en un sistema Linux utilizando software de código abierto?

He estado pensando en convertir los archivos PDF a texto sin formato en un directorio temporal con pdf2txt. Entonces podría filtrar todos los nombres de archivo que diff a bresultan más de x líneas. Pero esto no es elegante en absoluto y fallará con las publicaciones escaneadas. Las revistas a menudo no proporcionan texto de OCR para publicaciones antiguas.

También probé compareen el paquete ImageMagick, pero no pude manejar archivos PDF multipágina con esta herramienta.

diffpdf 2.1.1 hace un buen trabajo en una GUI en dos archivos, pero no pude descubrir cómo aplicarlo en muchos archivos, y las versiones recientes no están disponibles bajo ninguna licencia de código abierto.

Jonas Stein
fuente
1
Dado que hay enfoques muy diferentes entre las respuestas, podría ser bueno ser más específico y aclarar la pregunta. ¿Está buscando una forma sólida de comparar diferentes archivos pdf, incluidos documentos científicos, entre otros, o está tratando de encontrar una solución eficiente y elegante para comparar artículos de revistas, donde simplemente verificar si el título o el DOI coinciden es completamente suficiente?
inVader
Estoy buscando una solución similar: ahora estoy usando md5, que es problemático cuando cada descarga registra el tiempo y la ip en el pdf. Estoy trabajando en una solución con imagemagick con un script de contenedor para recorrer las páginas (y posiblemente intente omitir la primera página en caso de que sea el encabezado agregado por la revista). Estoy muy seguro de que esta es la solución más sólida posible. Usted sabe que funcionará muy bien porque es el mismo método que usa una persona cuando compara visualmente dos documentos. También es completamente independiente de la forma en que se genera el documento, solo su apariencia visual.
orion
También diría que una comparación de una sola página es probablemente suficiente: es poco probable que dos documentos sean diferentes si una página es la misma. La notación blah.pdf[1]llamará a la página deseada del documento.
orion
Si realmente necesita comparar archivos PDF donde uno o ambos se basan en el escaneo, creo que no puede evitar usar OCR. Muchos de los enfoques sugeridos aquí, por lo tanto, realmente no resuelven el problema.
gogoud

Respuestas:

4

Dado que diferentes editores usan diferentes métodos para "marcar" los archivos PDF, debe asegurarse de comparar sin tener en cuenta las marcas.

También necesita un método eficiente para comparar un nuevo PDF con todos los PDF ya descargados en caso de que descargue repetidamente el mismo PDF y, por ejemplo, esté marcado con la IP y / o la marca de fecha y hora que sugiere. No desea utilizar un mecanismo de comparación que consume mucho tiempo que compara cada nuevo PDF con muchos PDF ya descargados

Lo que necesita es una utilidad que elimine cada una de las posibles marcas y genere un hash de los datos restantes. Deberá mantener un mapa de nombre de archivo hash →, que puede estar en un archivo simple, y si un hash calculado ya está en el archivo, tiene un duplicado (y eliminarlo o hacer lo que sea necesario) y si el hash aún no está allí, agrega el hash y el nombre del archivo. El archivo se vería así:

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

Ese archivo es negligentemente pequeño en comparación con los PDF originales. Si tiene millones de archivos PDF, puede considerar almacenar estos datos en una base de datos. Por razones de eficiencia, es posible que desee incluir el tamaño del archivo y el número de páginas allí ( pdfinfo | egrep -E '^Pages:' | grep -Eo '[0-9]*').


Lo anterior empuja el problema a eliminar las marcas y generar el hash. Si sabe de dónde proviene el PDF al invocar la rutina de generación de hash (es decir, si realiza las descargas mediante programación), puede ajustar la generación de hash en función de eso. Pero incluso sin eso, hay varias posibilidades para la generación de hash:

  1. si los metadatos para el título y el autor no están vacíos y no incluyen cadenas no específicas como "Acrobat" o "PDF", podría generar el hash basado solo en la información del autor y el título. Use pdfinfo -E file.pdf | grep -E '^(Author:)|(Title:) | md5sumpara obtener el hash. También puede incluir el número de páginas al calcular el hash (' Pages:' en la pdfinfosalida).
  2. Si la regla anterior no funciona y el PDF contiene imágenes, extraiga las imágenes y genere un hash en los datos de imagen combinados. Si las imágenes alguna vez contienen texto en el pie de página o en el encabezado, como "Con licencia para el usuario Joe", elimine una X cantidad de líneas en la parte superior o inferior, antes de calcular el hash. Si esas marcas están en un gran texto de fondo gris con letras, esto, por supuesto, no funcionará, a menos que filtre los píxeles que no son totalmente negros (para eso podría usar imagemagick). Puede usar pdfimagespara extraer la información de la imagen en un archivo temporal.
  3. si las reglas anteriores no funcionan (porque no hay imágenes) puede usar pdftextpara extraer el texto, filtrar la marca (si filtra un poco demasiado, eso no es un problema) y luego generar el hash basado en ese.

Además, puede comparar si el tamaño del archivo antiguo encontrado a través del hash y ver si está dentro de ciertos márgenes con el nuevo archivo. La compresión y las ifferencias en cadenas (IP / fecha-hora-sello) solo deberían dar como resultado una diferencia de menos del uno por ciento.

Si conoce el método que utiliza el editor para determinar el hash, puede aplicar directamente el método "correcto" de lo anterior, pero incluso sin eso puede verificar los metadatos y aplicar algunas heurísticas, o determinar la cantidad de imágenes en un archivo y compare eso con el número de páginas (si están cerca, probablemente tenga un documento que consta de escaneos). pdftexten PDF escaneados de imágenes también tiene una salida reconocible.


Como base para trabajar, creé un paquete de Python que está en bitbucket y / o puede instalarse desde PyPI usando pip install ruamel.pdfdouble. Esto le proporciona el pdfdblcomando que realiza el escaneo como se describió anteriormente en metadatos, imágenes extraídas o en texto. Todavía no filtra las marcas (todavía) , pero el archivo Léame describe qué (dos) métodos mejorar para agregar eso.

El archivo Léame incluido:

ruamel.pdfdouble

Este paquete proporciona el pdfdblcomando:

pdfdbl scan dir1 dir2

Esto recorrerá los directorios proporcionados como argumento y para los archivos PDF encontrados, cree un hash basado en (en orden):

  • metadatos si son únicos
  • imágenes si el número de imágenes
  • texto

Esto supone que pdfinfo, pdfimages y pdftotext` del paquete poppler-utils están disponibles.

Se crea una "base de datos" en la ~/.config/pdfdbl/pdf.lstque se prueban más exploraciones.

Eliminar marcas

En ruamel/pdfdouble/pdfdouble.pyhay dos métodos que pueden ser mejoradas para filtrar las marcas en el PDF que los hacen menos único y hacer prácticamente los mismos archivos que tienen diferentes valores hash.

Para el texto, el método PdfData.filter_for_markingdebe extenderse para eliminar y marcar de la cadena que son sus argumentos y devolver el resultado.

Para las imágenes escaneadas, el método PdfData.process_image_and_updatedebe mejorarse, por ejemplo, cortando las líneas X inferiores y superiores de las imágenes, y eliminando cualquier texto de fondo gris configurando todos los píxeles negros a blancos. Esta función necesita actualizar el hash pasado usando el .update()método que pasa los datos filtrados.

Restricciones

La "base de datos" actual no puede manejar rutas que contienen nuevas líneas

Esta utilidad es actualmente solo Python 2.7.


Las partes de cadena conformes a IP se pueden sustituir con el remódulo de 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'
Anthon
fuente
En el pasado, también he usado el paquete python pdfrwpara extraer metadatos, pero eso no puede manejar archivos PDF cifrados, donde pdfinfosí.
Anthon
2

Daría pdftotextotra oportunidad, al menos para los archivos PDF en su colección que realmente tienen texto (de lo contrario, necesitaría ejecutar OCR), utilizando una herramienta mejor para procesar la salida.

Una vez que tenga su salida de texto (sucio), ejecútelo a través de un programa diseñado para determinar similitudes (en lugar de difflas diferencias línea por línea, que sería un camino rápido hacia la locura).

Considere algo como String :: Similarity de perl o el programa simhash (que está disponible en Debian pero no en Fedora / RHEL).

Adam Katz
fuente
2

Los archivos PDF contienen metadatos y acabo de comprobar una serie de documentos relacionados con la física de diferentes editores y todos tienen al menos el atributo "Título". Para algunos, el título es el título real de la publicación, para algunos contiene el DOI o identificadores similares. De todos modos, cada artículo que revisé contiene el título, y siempre es algo exclusivo de la publicación dada.

Puede utilizar pdftkpara acceder a los metadatos de los archivos PDF y compararlos. Para su propósito, esto definitivamente debería ser suficiente y es mucho más rápido que pdftotextsi el rendimiento es un problema. En caso de que un documento realmente no tenga metadatos de título, aún podría recurrir a él pdftotext.

Para volcar todos los metadatos en un archivo de texto (o stdout) para su posterior procesamiento, use

pdftk <PDF> dump_data output <TEXTFILE>

o consulte el manual para más opciones.

Si desea probar ImageMagick 's comparepero varias páginas causan un problema, también puede usar pdftkpara extraer páginas individuales y compararlas todas por separado (aunque tal vez solo una sola sea suficiente).

Aquí hay un fragmento de código que utiliza este enfoque para crear una diffsalida PDF similar a PDF de varias páginas: https://gist.github.com/mpg/3894692

invasor
fuente
1

¿Has mirado en PDF Content Comparer ? Hay opciones de línea de comandos que deberían permitirle automatizar el proceso.

Podría ejecutar algún tipo de lógica en el registro de diferencias que crea para ver qué tan similares son.

Si no puede, intente dividir los PDF en varios archivos temporalmente y compararlos de esa manera. Sin embargo, probablemente todavía tengas duplicados de esa manera. Un PDF puede tener una página en blanco adicional o algo que haga que todas las páginas posteriores se comparen como completamente diferentes.

Bratchley
fuente
Pueden ser las dos versiones más caras de este programa de código cerrado que pueden hacer el trabajo. Preferiría una solución de código abierto, aunque no es necesario que sea gratuita.
Jonas Stein
1

Después de una humilde contribución a la discusión (respuesta parcial):

Después de convertirlo a texto, usaría lo siguiente para calcular la similitud del archivo (basado en la diferencia de palabras):

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 resultado como

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

JJoao
fuente
1

Tengo un script que mira un pdf y primero intenta extraer el texto usando pdftotext, pero si esto falla (como lo hará con un documento escaneado), usa ghostscript para convertir un pdf escaneado de varias páginas en una serie de archivos png y luego usa tesseract para convertir esta serie en un solo archivo de texto. Si el escaneo es de calidad suficiente, hace un trabajo bastante bueno. Sería sencillo agregar código que compare el texto entre archivos, pero no he tenido este requisito.

ghostscript y tesseract son de código abierto y funcionan desde la línea de comandos.

gogoud
fuente
Puede extraer directamente las imágenes escaneadas utilizando pdfimagesel paquete poppler sin pérdida adicional de calidad que podría obtener al renderizar a través de ghostscript (que influye negativamente en cualquier OCR que desee hacer).
Anthon
@Anthon gracias por señalar esto, pero seguramente pdfimagessolo está haciendo lo mismo que ghostscript ( gs) aquí, es decir, extraer imágenes de pdf a jpg / png. ¿Por qué es mejor en esto que gs?
gogoud
La representación que hace ghostscript distorsiona los píxeles de las imágenes a menos que todos los escaneos tengan la misma resolución (no es el caso, por ejemplo, si se descartaron los bordes de los espacios en blanco) y solo si renderiza exactamente a la misma resolución que usan las imágenes
Anthon
@Anthon Interesante, he hecho algunas pruebas. Los resultados son muy similares, pero parece que gs/ tesseract(formato intermedio png) funciona un poco mejor que pdfimages/ tesseract(formato intermedio pbm). pdfimagesSin embargo, es más rápido.
gogoud
0

Ofrecería perl como solución. Hay un módulo llamado CAM::PDFque le permite extraer ... contenido PDF.

Funciona un poco así:

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

Puedes extraer el texto y compararlo.

Para documentos escaneados solamente: es mucho más difícil, pero suponiendo que estén usando las mismas imágenes base (por ejemplo, no las han escaneado por separado), probablemente pueda usar:

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

No lo he probado particularmente bien, porque no tengo sus documentos fuente. Sin embargo, creo que este enfoque debería ser útil: no estás comparando el contenido real de la imagen, porque ... bueno, eso es realmente difícil. Pero debería poder reconocer imágenes similares de los metadatos.

Para archivos PDF idénticos con metadatos diferentes, entonces algo simple como el hash del contenido del texto y los metadatos de la imagen debería ser suficiente.

Sobrique
fuente
-1

Hay una aplicación de Linux, llamada recoll . Puede realizar la tarea, pero solo para archivos PDF con capa de texto.

Annndrey
fuente
2
Para mí recollparece ser un motor de búsqueda de escritorio. No pude ver cómo usarlo para encontrar duplicados.
Jonas Stein
1
recollutiliza pdftotextpara manejar archivos PDF, que es lo que el OP está tratando de evitar aquí.
John WH Smith