Encontre arquivos PDF duplicados por conteúdo

9

Algumas revistas geram um PDF diferente para cada download. O APS, por exemplo, armazena o tempo e o endereço IP no PDF.

Ou há uma versão em papel com hiperlinks e outra com referências de texto.

Como é possível encontrar downloads duplicados de papéis com conteúdo igual a 90% em um sistema Linux usando software de código aberto?

Eu estive pensando em converter os arquivos PDF em texto sem formatação em um diretório temporário com pdf2txt. Então eu poderia filtrar todos os nomes de arquivos que diff a bresultem em mais de x linhas. Mas isso não é nada elegante e falhará com as publicações digitalizadas. Os periódicos geralmente não fornecem texto de OCR para publicações antigas.

Também tentei compareno pacote ImageMagick, mas não consegui lidar com arquivos PDF de várias páginas com esta ferramenta.

O diffpdf 2.1.1 faz um bom trabalho em uma GUI em dois arquivos, mas não consegui descobrir como aplicá-lo em muitos arquivos, e as versões recentes não estão disponíveis sob nenhuma licença de código aberto.

Jonas Stein
fonte
1
Como existem abordagens muito diferentes entre as respostas, pode ser bom ser mais específico e esclarecer a pergunta. Agora você está procurando uma maneira robusta de comparar arquivos PDF diferentes, incluindo artigos científicos, entre outros, ou está tentando encontrar uma solução eficiente e elegante para comparar artigos de periódicos, onde apenas verificar se o título ou o DOI está correspondendo é suficiente.
inVader 18/03/2015
Estou procurando uma solução semelhante - agora estou usando o md5, o que é problemático quando cada download registra o tempo e o ip no pdf. Estou trabalhando em uma solução com o imagemagick com um script de wrapper para percorrer as páginas (e, possivelmente, tentar pular a primeira página, caso seja o cabeçalho adicionado pelo diário). Estou muito confiante de que esta é a solução mais robusta possível. Você sabe que funcionará muito bem porque é o mesmo método que uma pessoa usa ao comparar visualmente dois documentos. Também é completamente independente da maneira como o documento é gerado, apenas sua aparência visual.
orion
Eu diria também que uma única comparação de página é provavelmente suficiente - é improvável que dois documentos sejam diferentes se uma página for a mesma. A notação blah.pdf[1]chamará a página desejada do documento.
orion
Se você realmente precisa comparar PDFs em que um ou ambos são baseados na digitalização, acho que não pode evitar o uso do OCR. Muitas das abordagens sugeridas aqui, portanto, realmente não resolvem o problema.
gogoud

Respostas:

4

Como editores diferentes usam métodos diferentes para "marcar" os PDFs, é necessário comparar sem levar em consideração as marcações.

Você também precisa de um método eficiente para comparar um novo PDF com todos os PDFs já baixados, caso faça o download repetido do mesmo PDF e, por exemplo, esteja marcado com o IP e / ou carimbo de data / hora, conforme sugerido. Você não deseja usar um mecanismo de comparação demorado que compara cada novo PDF com muitos PDFs já baixados

O que você precisa é de um utilitário que retire cada uma das marcações possíveis e gere um hash dos dados restantes. Você precisará manter um mapa de nome de arquivo hash →, que pode estar em um arquivo simples, e se um hash computado já estiver no arquivo, você terá uma duplicata (e a excluirá ou fará o que for necessário) e se o hash ainda não estiver lá, você adiciona o hash e o nome do arquivo. O arquivo seria algo como:

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

Esse arquivo é negligentemente pequeno comparado aos PDFs originais. Se você possui milhões de PDFs, considere armazenar esses dados em um banco de dados. Por questões de eficiência, convém incluir o tamanho do arquivo e o número de páginas ( pdfinfo | egrep -E '^Pages:' | grep -Eo '[0-9]*').


O procedimento acima elimina o problema de remover as marcações e gerar o hash. Se você souber de onde vem o PDF ao chamar a rotina de geração de hash (ou seja, se você fizer os downloads programaticamente), poderá ajustar a geração de hash com base nisso. Mas mesmo sem isso, existem várias possibilidades para geração de hash:

  1. se os metadados do título e do autor não estiverem vazios e não incluirem sequências não específicas como "Acrobat" ou "PDF", você poderá gerar o hash com base apenas nas informações do autor e do título. Use pdfinfo -E file.pdf | grep -E '^(Author:)|(Title:) | md5sumpara obter o hash. Você também pode incluir o número de páginas no cálculo do hash (' Pages:' na pdfinfosaída).
  2. se a regra anterior não funcionar e o PDF contiver imagens, extraia as imagens e gere um hash nos dados da imagem combinada. Se as imagens contiverem texto no rodapé ou cabeçalho como "Licenciado para o usuário Joe", retire um número X de linhas na parte superior ou inferior, antes de calcular o hash. Se essas marcações estiverem em algum grande texto em segundo plano acinzentado, é claro que isso não funcionará, a menos que você filtre os pixels que não são totalmente pretos (pelo que você pode usar imagemagick). Você pode usar pdfimagespara extrair as informações da imagem em um arquivo temporário.
  3. se as regras anteriores não funcionarem (porque não há imagens), você pode usar pdftextpara extrair o texto, filtrar a marcação (se você filtrar um pouco demais, isso não é um problema) e depois gerar o hash com base em aquele.

Além disso, você pode comparar se o tamanho do arquivo antigo encontrado via hash e verificar se está dentro de determinadas margens com o novo arquivo. Compactação e ifferências em seqüências de caracteres (IP / data e hora) só devem resultar em menos de um por cento de diferença.

Se você conhece o método que o editor usa ao determinar o hash, é possível aplicar diretamente o método "correto" acima, mas mesmo sem isso, você pode verificar os metadados e aplicar algumas heurísticas ou determinar o número de imagens em um arquivo e compare isso com o número de páginas (se estiverem próximas, você provavelmente terá um documento que consiste em digitalizações). pdftextna imagem digitalizada, os PDFs também têm uma saída reconhecível.


Como base para trabalhar, criei um pacote python que está no bitbucket e / ou pode ser instalado no PyPI usando pip install ruamel.pdfdouble. Isso fornece o pdfdblcomando que faz a digitalização conforme descrito acima em metadados, imagens extraídas ou texto. Ele não faz qualquer filtragem de marcas (ainda) , mas o readme descreve quais (dois) métodos para aumentar a não acrescentar que.

O leia-me incluído:

ruamel.pdfdouble

este pacote fornece o pdfdblcomando:

pdfdbl scan dir1 dir2

Isso percorrerá os diretórios fornecidos como argumento e, para os arquivos PDF encontrados, criará um hash com base em (em ordem):

  • metadados se exclusivos
  • imagens se o número de imagens
  • texto

Isso pressupõe que pdfinfo, pdfimages e pdftotext` do pacote poppler-utils estejam disponíveis.

Um "banco de dados" é construído ~/.config/pdfdbl/pdf.lstcontra o qual outras verificações são testadas.

Removendo marcações

No ruamel/pdfdouble/pdfdouble.pyexistem dois métodos que podem ser melhoradas para filtrar marcas no PDF que torná-los menos exclusivo e fazer praticamente os mesmos arquivos para ter diferentes hashes.

Para texto, o método PdfData.filter_for_markingdeve ser estendido para remover e marcações da sequência que é seus argumentos e retornar o resultado.

Para imagens digitalizadas, o método PdfData.process_image_and_updateprecisa ser aprimorado, por exemplo, cortando as linhas X inferior e superior da imagem e removendo qualquer texto cinza de fundo, definindo todos os pixels pretos para branco. Essa função precisa atualizar o hash passado usando o .update()método que passa nos dados filtrados.

Restrições

O "banco de dados" atual não pode manipular caminhos que contêm novas linhas

Atualmente, este utilitário é apenas Python 2.7.


As partes de string em conformidade com o IP podem ser substituídas pelo remódulo do 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
fonte
No passado, usei o pacote python pdfrwpara extrair metadados também, mas isso não pode lidar com arquivos pdf criptografados, onde pdfinfopode.
Anthon
2

Eu daria pdftotextoutra chance, pelo menos para os PDFs em sua coleção que realmente possuem texto (caso contrário, você precisaria executar o OCR), usando uma ferramenta melhor para processar a saída.

Depois de obter sua saída de texto (suja), execute-a através de um programa projetado para determinar semelhanças (em vez diffdas diferenças linha por linha, o que seria um caminho rápido para a loucura).

Considere algo como o String: Similarity do perl ou o programa simhash (que está disponível no Debian, mas não no Fedora / RHEL).

Adam Katz
fonte
2

Os PDFs contêm metadados e acabei de verificar vários artigos relacionados à física de diferentes editores e todos eles têm pelo menos o atributo "Título". Para alguns, o título é o título real da publicação, para alguns, contém o DOI ou identificadores semelhantes. De qualquer forma, todo artigo que verifiquei contém o título, e é sempre algo único para a publicação em questão.

Você pode usar pdftkpara acessar os metadados dos PDFs e compará-los. Para seu propósito, isso definitivamente deve ser suficiente e é muito mais rápido do que pdftotextse o desempenho fosse um problema. Caso um artigo realmente não deva ter metadados de título, você ainda poderá recorrer a pdftotext.

Para despejar todos os metadados em um arquivo de texto (ou stdout) para processamento adicional, use

pdftk <PDF> dump_data output <TEXTFILE>

ou consulte o manual para mais opções.

Se você quiser experimentar o ImageMagick , comparemas várias páginas causam um problema, você também pode usar pdftkpara extrair páginas únicas e comparar todas separadamente (talvez apenas comparar uma única seja suficiente).

Aqui está um trecho de código que usa essa abordagem para criar uma diffsaída semelhante a PDF para PDFs de várias páginas: https://gist.github.com/mpg/3894692

invasor
fonte
1

Você já olhou para o PDF Content Comparer ? Existem opções de linha de comando que devem permitir que você automatize o processo.

Você pode executar algum tipo de lógica no log de diferenças criado para ver como elas são semelhantes.

Caso contrário, tente dividir os PDFs em vários arquivos temporariamente e compará-los dessa maneira. Você provavelmente ainda teria duplicatas dessa maneira, no entanto. Um PDF pode ter apenas uma página em branco extra ou algo que faria com que todas as páginas subseqüentes fossem comparadas como completamente diferentes.

Bratchley
fonte
Pode ser que as duas versões mais caras deste programa de código fechado possam fazer o trabalho. Eu preferiria uma solução de código aberto, embora não precise ser gratuita.
Jonas Stein
1

Após uma humilde contribuição para a discussão (resposta parcial):

Depois de convertido em texto, usaria o seguinte para calcular a smilaridade do arquivo (com base na diferença de palavras):

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

(1) produz um 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
fonte
1

Eu tenho um script que analisa um pdf e primeiro tenta extrair texto usando pdftotext, mas se isso falhar (como acontece com um documento digitalizado), ele usa o ghostscript para transformar um PDF digitalizado com várias páginas em uma série de arquivos png e, em seguida, usa o tesseract para converter esta série em um único arquivo de texto. Se a digitalização for de qualidade suficiente, ele faz um bom trabalho. Seria fácil adicionar código comparando o texto entre arquivos, mas eu não tive esse requisito.

O ghostscript e o tesseract são de código aberto e funcionam a partir da linha de comando.

gogoud
fonte
Você pode extrair diretamente as imagens digitalizadas usando pdfimageso pacote poppler sem perda adicional de qualidade que poderia obter com a renderização através do ghostscript (que influencia negativamente qualquer OCR que você deseja fazer).
Anthon
@ Anthon obrigado por apontar isso, mas certamente pdfimagesestá fazendo o mesmo que ghostscript ( gs) aqui, isto é, extraindo imagens de pdf para jpg / png. Por que é melhor nisso do que gs?
gogoud
A renderização que o ghostscript distorce os pixels das imagens, a menos que todas as digitalizações tenham a mesma resolução (não é o caso, por exemplo, se as bordas dos espaços em branco foram descartadas) e somente se você renderizar exatamente na mesma resolução que as imagens usam
Anthon
@Anthon Interessante, eu fiz um pequeno teste. Os resultados são muito semelhantes, mas parece que gs/ / tesseract(formato intermediário png) funciona um pouco melhor que pdfimages/ tesseract(formato intermediário pbm). pdfimagesé mais rápido.
gogoud
0

Eu ofereceria o perl como uma solução. Existe um módulo chamado CAM::PDFque permite extrair ... conteúdo em PDF.

Funciona um pouco assim:

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

Você pode extrair o texto e comparar isso.

Para documentos digitalizados apenas - é muito mais difícil, mas supondo que eles estejam usando as mesmas imagens de base (por exemplo, não as digitalizou separadamente), provavelmente você poderá 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;
}

Não testei particularmente bem, porque não tenho seus documentos de origem. Eu acho que essa abordagem deve funcionar - você não está comparando o conteúdo real da imagem, porque ... bem, isso é realmente difícil. Mas você deve conseguir reconhecer imagens semelhantes dos metadados.

Para PDFs idênticos com metadados diferentes, algo simples como hash do conteúdo do texto e dos metadados da imagem deve ser suficiente.

Sobrique
fonte
-1

Há um aplicativo Linux, chamado recoll . Ele pode executar a tarefa, mas apenas para PDFs com camada de texto.

Annndrey
fonte
2
Para mim, recollparece ser um mecanismo de busca na área de trabalho. Eu não conseguia ver como usá-lo para encontrar duplicatas.
Jonas Stein
1
recollusa pdftotextpara lidar com PDFs, que é o que o OP está tentando evitar aqui.
John WH Smith