Estrazione dati Python da un PDF crittografato


12

Sono un neolaureato in matematica pura che ha frequentato solo pochi corsi di programmazione di base. Sto facendo uno stage e ho un progetto interno di analisi dei dati. Devo analizzare i PDF interni degli ultimi anni. I PDF sono "protetti". In altre parole, sono crittografati. Non abbiamo password PDF, ancora di più, non siamo sicuri che esistano password. Ma abbiamo tutti questi documenti e possiamo leggerli manualmente. Possiamo anche stamparli. L'obiettivo è leggerli con Python perché è il linguaggio che abbiamo qualche idea.

Innanzitutto, ho provato a leggere i PDF con alcune librerie Python. Tuttavia, le librerie Python che ho trovato non leggono PDF crittografati. A quel tempo, non potevo nemmeno esportare le informazioni usando Adobe Reader.

In secondo luogo, ho deciso di decrittografare i PDF. Ho avuto successo usando la libreria Python pykepdf. Pykepdf funziona molto bene! Tuttavia, i PDF decrittografati non possono essere letti anche con le librerie Python del punto precedente ( PyPDF2 e Tabula ). In questo momento, abbiamo apportato alcuni miglioramenti perché utilizzando Adobe Reader posso esportare le informazioni dai PDF decrittografati, ma l'obiettivo è fare tutto con Python.

Il codice che sto mostrando funziona perfettamente con i PDF non crittografati, ma non con i PDF crittografati. Non funziona anche con i PDF decrittografati ottenuti con pykepdf.

Non ho scritto il codice. L'ho trovato nella documentazione delle librerie Python Pykepdf e Tabula . La soluzione PyPDF2 è stata scritta da Al Sweigart nel suo libro " Automate the Boring Stuff with Python ", che consiglio vivamente. Ho anche verificato che il codice funzioni correttamente, con i limiti che ho spiegato prima.

Prima domanda, perché non riesco a leggere i file decifrati, se i programmi funzionano con file che non sono mai stati crittografati?

Seconda domanda, possiamo leggere in qualche modo con Python i file decifrati? Quale libreria può farlo o è impossibile? Tutti i PDF decrittografati sono estraibili?

Grazie per il tuo tempo e aiuto !!!

Ho trovato questi risultati usando Python 3.7, Windows 10, Jupiter Notebooks e Anaconda 2019.07.

Python

import pikepdf
with pikepdf.open("encrypted.pdf") as pdf:
  num_pages = len(pdf.pages)
  del pdf.pages[-1]
  pdf.save("decrypted.pdf")

import tabula
tabula.read_pdf("decrypted.pdf", stream=True)

import PyPDF2
pdfFileObj=open("decrypted.pdf", "rb")
pdfReader=PyPDF2.PdfFileReader(pdfFileObj)
pdfReader.numPages
pageObj=pdfReader.getPage(0)
pageObj.extractText()

Con Tabula, ricevo il messaggio "il file di output è vuoto".

Con PyPDF2, ottengo solo '/ n'

AGGIORNAMENTO 03/10/2019 Pdfminer.six (Versione novembre 2018)

Ho ottenuto risultati migliori usando la soluzione pubblicata da DuckPuncher . Per il file decrittografato, ho ottenuto le etichette, ma non i dati. Lo stesso succede con il file crittografato. Per il file che non è mai stato crittografato funziona perfettamente. Poiché ho bisogno dei dati e delle etichette dei file crittografati o decrittografati, questo codice non funziona per me. Per tale analisi, ho usato pdfminer.six che è la libreria Python che è stata rilasciata a novembre 2018. Pdfminer.six include una libreria pycryptodome. Secondo la loro documentazione " PyCryptodome è un pacchetto Python autonomo di primitive crittografiche di basso livello .."

Il codice è nella domanda di scambio di stack: estrarre testo da un file PDF usando PDFMiner in Python?

Mi piacerebbe se volessi ripetere il mio esperimento. Ecco la descrizione:

1) Esegui i codici menzionati in questa domanda con qualsiasi PDF che non sia mai stato crittografato.

2) Fai lo stesso con un PDF "Sicuro" (questo è un termine usato da Adobe), lo chiamo PDF crittografato. Utilizza un modulo generico che puoi trovare utilizzando Google. Dopo averlo scaricato, è necessario compilare i campi. Altrimenti, verifichi le etichette, ma non i campi. I dati sono nei campi.

3) Decifrare il PDF crittografato utilizzando Pykepdf. Questo sarà il PDF decrittografato.

4) Eseguire nuovamente i codici utilizzando il PDF decrittografato.

AGGIORNAMENTO 04/10/2019 Camelot (Versione luglio 2019)

Ho trovato la libreria Python Camelot. Fai attenzione che hai bisogno di Camelot-Py 0.7.3.

È molto potente e funziona con Python 3.7. Inoltre, è molto facile da usare. Innanzitutto, devi anche installare Ghostscript . Altrimenti, non funzionerà. Devi anche installare Panda . Non utilizzare pip install camelot-py . Invece usa pip install camelot-py [cv]

L'autore del programma è Vinayak Mehta. Frank Du condivide questo codice in un video di YouTube "Estrai dati tabulari da PDF con Camelot utilizzando Python."

Ho controllato il codice e funziona con file non crittografati. Tuttavia, non funziona con file crittografati e decrittografati, e questo è il mio obiettivo .

Camelot è orientato per ottenere tabelle da PDF.

Ecco il codice:

Python

import camelot
import pandas
name_table = camelot.read_pdf("uncrypted.pdf")
type(name_table)

#This is a Pandas dataframe
name_table[0]

first_table = name_table[0]   

#Translate camelot table object to a pandas dataframe
first_table.df

first_table.to_excel("unencrypted.xlsx")
#This creates an excel file.
#Same can be done with csv, json, html, or sqlite.

#To get all the tables of the pdf you need to use this code.
for table in name_table:
   print(table.df)

AGGIORNAMENTO 07/10/2019 Ho trovato un trucco. Se apro il pdf protetto con Adobe Reader e lo stampo utilizzando Microsoft in PDF e lo salvo in formato PDF, posso estrarre i dati utilizzando quella copia. Posso anche convertire il file PDF in JSON, Excel, SQLite, CSV, HTML e altri formati. Questa è una possibile soluzione alla mia domanda. Tuttavia, sto ancora cercando un'opzione per farlo senza quel trucco perché l'obiettivo è farlo al 100% con Python. Sono anche preoccupato che se si utilizza un metodo di crittografia migliore, il trucco potrebbe non funzionare. A volte è necessario utilizzare più volte Adobe Reader per ottenere una copia estraibile.

AGGIORNAMENTO 10/8/2019. Terza domanda Ora ho una terza domanda. Tutti i pdf protetti / crittografati sono protetti da password? Perché pikepdf non funziona? La mia ipotesi è che l'attuale versione di pikepdf possa rompere qualche tipo di crittografia, ma non tutte. @constt ha affermato che PyPDF2 può rompere qualche tipo di protezione. Tuttavia, gli ho risposto che ho trovato un articolo che PyPDF2 può violare le crittografie fatte con Adobe Acrobat Pro 6.0, ma no con le versioni posteriori.


2
Non ho potuto riprodurre questi problemi con PyPDF2, tutto funziona bene. Ho usato pdftkcosì come i servizi online per crittografare i file. Puoi pubblicare collegamenti a file pdf "fastidiosi"?
const

1
Ok grazie! Hai provato a usare qpdfper decrittografare i tuoi file? Nel caso in cui farà il trucco, puoi chiamarlo dal tuo script usando il subprocessmodulo per decrittografare i file prima di analizzarli.
const

1
Innanzitutto, PyPDF2 non può decodificare i file PDF di Acrobat => 6.0. In secondo luogo, pikepdf al momento non ha un attrezzo per l'estrazione del testo.
vita è complessa il

1
@Beginner Vorrei ipotizzare che ciò abbia a che fare con la formattazione sottostante utilizzata da pykepdf per scrivere il PDF non crittografato.
vita è complessa il

2
"Tutti i pdf protetti / crittografati sono protetti da password?" - no. Ci sono anche pdf crittografati usando la crittografia a chiave privata / pubblica basata sui certificati X509.
mkl

Risposte:


8

ULTIMO AGGIORNAMENTO 10-11-2019

Non sono sicuro di aver compreso completamente la tua domanda. Il codice seguente può essere perfezionato, ma legge in un PDF crittografato o non crittografato ed estrae il testo. Per favore fatemi sapere se ho frainteso le vostre esigenze.

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def extract_encrypted_pdf_text(path, encryption_true, decryption_password):

  output = StringIO()

  resource_manager = PDFResourceManager()
  laparams = LAParams()

  device = TextConverter(resource_manager, output, codec='utf-8', laparams=laparams)

  pdf_infile = open(path, 'rb')
  interpreter = PDFPageInterpreter(resource_manager, device)

  page_numbers = set()

  if encryption_true == False:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, caching=True, check_extractable=True):
      interpreter.process_page(page)

  elif encryption_true == True:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, password=decryption_password, caching=True, check_extractable=True):
      interpreter.process_page(page)

 text = output.getvalue()
 pdf_infile.close()
 device.close()
 output.close()
return text

results = extract_encrypted_pdf_text('encrypted.pdf', True, 'password')
print (results)

Ho notato che nel tuo codice pikepdf utilizzato per aprire un PDF crittografato mancava una password, che avrebbe dovuto generare questo messaggio di errore:

pikepdf._qpdf.PasswordError: crittografato.pdf: password non valida

import pikepdf

with pikepdf.open("encrypted.pdf", password='password') as pdf:
num_pages = len(pdf.pages)
del pdf.pages[-1]
pdf.save("decrypted.pdf")

Puoi usare tika per estrarre il testo dal decrypted.pdf creato da pikepdf .

from tika import parser

parsedPDF = parser.from_file("decrypted.pdf")
pdf = parsedPDF["content"]
pdf = pdf.replace('\n\n', '\n')

Inoltre, al momento pikepdf non implementa l'estrazione del testo, tra cui l'ultima versione v1.6.4.


Ho deciso di eseguire un paio di test utilizzando vari file PDF crittografati.

Ho chiamato tutti i file crittografati "crittografati.pdf" e tutti hanno usato la stessa password di crittografia e decrittografia.

  1. Adobe Acrobat 9.0 e versioni successive - livello di crittografia AES a 256 bit

    • pikepdf è stato in grado di decrittografare questo file
    • PyPDF2 non è riuscito a estrarre il testo correttamente
    • tika potrebbe estrarre correttamente il testo
  2. Adobe Acrobat 6.0 e versioni successive - livello di crittografia RC4 a 128 bit

    • pikepdf è stato in grado di decrittografare questo file
    • PyPDF2 non è riuscito a estrarre il testo correttamente
    • tika potrebbe estrarre correttamente il testo
  3. Adobe Acrobat 3.0 e versioni successive - livello di crittografia RC4 a 40 bit

    • pikepdf è stato in grado di decrittografare questo file
    • PyPDF2 non è riuscito a estrarre il testo correttamente
    • tika potrebbe estrarre correttamente il testo
  4. Adobe Acrobat 5.0 e versioni successive - livello di crittografia RC4 a 128 bit

    • creato con Microsoft Word
    • pikepdf è stato in grado di decrittografare questo file
    • PyPDF2 potrebbe estrarre correttamente il testo
    • tika potrebbe estrarre correttamente il testo
  5. Adobe Acrobat 9.0 e versioni successive - livello di crittografia AES a 256 bit

    • creato usando pdfprotectfree
    • pikepdf è stato in grado di decrittografare questo file
    • PyPDF2 potrebbe estrarre correttamente il testo
    • tika potrebbe estrarre correttamente il testo

PyPDF2 è stato in grado di estrarre testo da file PDF decrittografati non creati con Adobe Acrobat.

Suppongo che gli errori abbiano qualcosa a che fare con la formattazione incorporata nei PDF creati da Adobe Acrobat. Sono necessari ulteriori test per confermare questa congettura sulla formattazione.

tika è stata in grado di estrarre il testo da tutti i documenti decifrati con pikepdf.


 import pikepdf
 with pikepdf.open("encrypted.pdf", password='password') as pdf:
    num_pages = len(pdf.pages)
    del pdf.pages[-1]
    pdf.save("decrypted.pdf")


 from PyPDF2 import PdfFileReader

 def text_extractor(path):
   with open(path, 'rb') as f:
     pdf = PdfFileReader(f)
     page = pdf.getPage(1)
     print('Page type: {}'.format(str(type(page))))
     text = page.extractText()
     print(text)

    text_extractor('decrypted.pdf')

PyPDF2 non può decrittografare i file PDF di Acrobat => 6.0

Questo problema è stato aperto con i proprietari dei moduli, dal 15 settembre 2015 . Non è chiaro nei commenti relativi a questo problema quando questo problema verrà risolto dai proprietari del progetto. L'ultimo commit è stato il 25 giugno 2018.

Problemi di decodifica PyPDF4

PyPDF4 è il sostituto di PyPDF2. Questo modulo presenta anche problemi di decodifica con alcuni algoritmi utilizzati per crittografare i file PDF.

file di test: Adobe Acrobat 9.0 e versioni successive - livello di crittografia AES a 256 bit

Messaggio di errore PyPDF2: sono supportati solo i codici algoritmo 1 e 2

Messaggio di errore PyPDF4: sono supportati solo i codici algoritmo 1 e 2. Questo PDF utilizza il codice 5


SEZIONE DI AGGIORNAMENTO 10-11-2019

Questa sezione è in risposta ai tuoi aggiornamenti del 10-07-2019 e 10-08-2019.

Nel tuo aggiornamento hai dichiarato di poter aprire un "pdf protetto con Adobe Reader" e stampare il documento su un altro PDF, che rimuove il flag "SICURO". Dopo aver fatto alcuni test, credo che abbiano capito cosa sta succedendo in questo scenario.

Livello di sicurezza dei PDF Adobe

I PDF Adobe hanno diversi tipi di controlli di sicurezza che possono essere abilitati dal proprietario del documento. I controlli possono essere applicati con una password o un certificato.

  1. Crittografia dei documenti (applicata con una password di apertura del documento)

    • Crittografa tutti i contenuti del documento (più comuni)
    • Crittografa tutto il contenuto del documento tranne i metadati => Acrobat 6.0
    • Crittografa solo i file allegati => Acrobat 7.0
  2. Modifica e stampa restrittive (imposta con una password di autorizzazione)

    • Stampa consentita
    • Modifiche consentite

L'immagine seguente mostra un Adobe PDF crittografato con la crittografia AES a 256 bit. Per aprire o stampare questo PDF è richiesta una password. Quando aprite questo documento in Adobe Reader con la password, il titolo indicherà SICURO

password_level_encryption

Questo documento richiede l'apertura di una password con i moduli Python menzionati in questa risposta. Se si tenta di aprire un PDF crittografato con Adobe Reader. Dovresti vedere questo:

password_prompt

Se non ricevi questo avviso, il documento non ha i controlli di sicurezza abilitati o ha solo quelli di modifica e stampa restrittivi abilitati.

L'immagine seguente mostra la modifica restrittiva abilitata con una password in un documento PDF. Nota la stampa è abilitata . Per aprire o stampare questo PDF non è richiesta una password . Quando aprite questo documento in Adobe Reader senza password, il titolo indicherà SECURED Questo è lo stesso avvertimento del PDF crittografato che è stato aperto con una password.

Quando si stampa questo documento su un nuovo PDF, l' avviso SECURED viene rimosso perché la modifica restrittiva è stata rimossa.

password_level_restrictive_editing

Tutti i prodotti Adobe applicano le restrizioni stabilite dalla password delle autorizzazioni. Tuttavia, se i prodotti di terze parti non supportano queste impostazioni, i destinatari dei documenti sono in grado di ignorare alcune o tutte le restrizioni impostate.

Quindi presumo che il documento che si sta stampando su PDF abbia la modifica restrittiva abilitata e che non sia necessaria una password per aprirla.

Per quanto riguarda la violazione della crittografia PDF

PyPDF2PyPDF4 sono progettati per interrompere la funzione di password aperta del documento di un documento PDF. Entrambi i moduli genereranno il seguente errore se tentano di aprire un file PDF protetto da password crittografato.

PyPDF2.utils.PdfReadError: il file non è stato decrittografato

La funzione di apertura della password di un file PDF crittografato può essere ignorata utilizzando una varietà di metodi, ma una singola tecnica potrebbe non funzionare e alcuni non saranno accettabili a causa di diversi fattori, inclusa la complessità della password.

La crittografia PDF funziona internamente con chiavi di crittografia da 40, 128 o 256 bit a seconda della versione PDF. La chiave di crittografia binaria deriva da una password fornita dall'utente. La password è soggetta a vincoli di lunghezza e codifica.

Ad esempio, PDF 1.7 Adobe Extension Level 3 (Acrobat 9 - AES-256) ha introdotto i caratteri Unicode (65.536 caratteri possibili) e ha aumentato la lunghezza massima di 127 byte nella rappresentazione UTF-8 della password.


Il codice seguente aprirà un PDF con la modifica restrittiva abilitata. Salverà questo file in un nuovo PDF senza l'aggiunta dell'avviso SICURO. Il codice tika analizzerà il contenuto del nuovo file.

from tika import parser
import pikepdf

# opens a PDF with restrictive editing enabled, but that still 
# allows printing.
with pikepdf.open("restrictive_editing_enabled.pdf") as pdf:
  pdf.save("restrictive_editing_removed.pdf")

  # plain text output
  parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf", xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  print (pdf)

Questo codice controlla se è richiesta una password per l'apertura del file. Questo codice deve essere perfezionato e altre funzioni possono essere aggiunte. Ci sono molte altre funzionalità che possono essere aggiunte, ma la documentazione per pikepdf non corrisponde ai commenti all'interno della base di codice, quindi sono necessarie ulteriori ricerche per migliorarlo.

# this would be removed once logging is used
############################################
import sys
sys.tracebacklimit = 0
############################################

import pikepdf
from tika import parser

def create_pdf_copy(pdf_file_name):
  with pikepdf.open(pdf_file_name) as pdf:
    new_filename = f'copy_{pdf_file_name}'
    pdf.save(new_filename)
    return  new_filename

def extract_pdf_content(pdf_file_name):
  # plain text output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  parsedPDF = parser.from_file(pdf_file_name, xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  return pdf

def password_required(pdf_file_name):
  try:
    pikepdf.open(pdf_file_name)

  except pikepdf.PasswordError as error:
    return ('password required')

  except pikepdf.PdfError as results:
    return ('cannot open file')


filename = 'decrypted.pdf'
password = password_required(filename)
if password != None:
  print (password)
elif password == None:
  pdf_file = create_pdf_copy(filename)
  results = extract_pdf_content(pdf_file)
  print (results)

2
Come stai aprendo un file PDF sicuro senza fornire una password?
vita è complessa il

1
Ti riferisci solo alla protezione di modifica restrittiva?
vita è complessa il

1
Risposta aggiornata con codice che ha funzionato con un PDF con protezione di modifica restrittiva abilitata, ma che consente la stampa.
vita è complessa il

1
Puoi usare XHTML?
vita è complessa il

1
Ho modificato la risposta all'output XHTML. JSON è possibile, ma richiede di scavare nel codice del progetto github relativo al parser tika.
vita è complessa il

1

Puoi provare a gestire l'errore generato da questi file quando apri questi file senza password.

import pikepdf

def open_pdf(pdf_file_path, pdf_password=''):
    try:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path)

    except pikepdf._qpdf.PasswordError:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path, password=pdf_password)

    finally:
        return pdf_obj

È possibile utilizzare il pdf_obj restituito per il lavoro di analisi. Inoltre, puoi fornire la password nel caso in cui tu abbia un PDF crittografato.


1
La ringrazio per la risposta! Stiamo provando a leggerlo senza password. In questo momento, siamo stati in grado di farlo con il metodo che è stato spiegato nel mio AGGIORNAMENTO 10/7/2019
Principiante

Questo è lungi dal rispondere alla domanda. Sembra che tu non abbia letto la domanda completa.
shoonya ek,

1
Gestisce i PDF protetti in cui normalmente pikepdf ha esito negativo quando il valore predefinito della password è Nessuno. Passando una stringa vuota è in grado di aprire e analizzare correttamente un documento PDF protetto (nei casi di test che ho eseguito).
Mahendra Singh,

1
@Beginner in questo caso non è necessario convertire i PDF qui. Questo è solo dalla mia precedente esperienza in cui i PDF protetti funzionano fornendo una password vuota.
Mahendra Singh,

1
@Binner questo è il mio intero codice. Questo restituisce solo il pdf_object da pikepdf. Nel caso in cui si desideri salvare questo pdf, è sufficiente salvare l'oggetto restituito utilizzando pdf_obj.save ('your_file_path'). Successivamente, è possibile utilizzare questo PDF per analizzare il testo e altri oggetti. Uso una libreria chiamata PdfPlumber per l'estrazione del testo.
Mahendra Singh,

1

Per tabula-py, puoi provare l'opzione password con read_pdf. Dipende dalla funzione di tabula-java, quindi non sono sicuro di quale crittografia sia supportata.

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.