Извлечение данных Python из зашифрованного PDF - PullRequest
10 голосов
/ 03 октября 2019

Я недавний выпускник по чистой математике, который прошел только несколько базовых курсов по программированию. Я прохожу стажировку и у меня есть проект по анализу внутренних данных. Я должен проанализировать внутренние PDF-файлы последних лет. PDF-файлы "защищены". Другими словами, они зашифрованы. У нас нет паролей PDF, тем более, мы не уверены, существуют ли пароли. Но у нас есть все эти документы, и мы можем прочитать их вручную. Мы можем распечатать их также. Цель состоит в том, чтобы прочитать их с помощью Python, потому что это язык, который у нас есть.

Сначала я попытался прочитать PDF-файлы с помощью некоторых библиотек Python. Однако библиотеки Python, которые я обнаружил, не читают зашифрованные PDF-файлы. В то время я тоже не мог экспортировать информацию с помощью Adobe Reader.

Во-вторых, я решил расшифровать PDF-файлы. Я успешно использовал библиотеку Python pykepdf. Pykepdf работает очень хорошо! Тем не менее, дешифрованные PDF не могут быть прочитаны также с библиотеками Python предыдущего пункта ( PyPDF2 и Tabula ). В настоящее время мы добились некоторого улучшения, поскольку с помощью Adobe Reader я могу экспортировать информацию из дешифрованных PDF-файлов, но цель состоит в том, чтобы сделать все с помощью Python.

Код, который я показываю, прекрасно работает с незашифрованными PDF-файлами. , но не с зашифрованными PDF-файлами. Он также не работает с расшифрованными PDF-файлами, которые были получены с помощью pykepdf.

Я не писал код. Я нашел это в документации библиотек Python Pykepdf и Tabula . Решение PyPDF2 было написано Аль-Суигартом в его книге " Автоматизировать скучный материал с помощью Python ", который я очень рекомендую. Я также проверил, что код работает нормально, с ограничениями, которые я объяснил ранее.

Первый вопрос, почему я не могу прочитать расшифрованные файлы, если программы работают с файлами, которые никогда не имелибыл зашифрован?

Второй вопрос, можем ли мы как-нибудь прочитать с помощью Python расшифрованные файлы? Какая библиотека может это сделать или это невозможно? Извлекаются ли все расшифрованные PDF-файлы?

Спасибо за ваше время и помощь !!!

Я нашел эти результаты с помощью Python 3.7, Windows 10, ноутбуков Jupiter и ноутбуков Anaconda2019.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()

В Tabula я получаю сообщение «Выходной файл пуст.»

В PyPDF2 я получаю только '/ n'

ОБНОВЛЕНИЕ 3/3/2019 Pdfminer.six (версия от ноября 2018 г.)

Я получил лучшие результаты, используя решение, опубликованное DuckPuncher . Для расшифрованного файла я получил метки, но не данные. То же самое происходит с зашифрованным файлом. Для файла, который никогда не был зашифрован, работает идеально. Поскольку мне нужны данные и метки зашифрованных или дешифрованных файлов, этот код не работает для меня. Для этого анализа я использовал pdfminer.six , то есть библиотека Python, выпущенная в ноябре 2018 года. Pdfminer.six включает в себя библиотеку pycryptodome. Согласно их документации " PyCryptodome является автономным пакетом Python низкоуровневых криптографических примитивов .."

Код находится в вопросе обмена стека: Извлечение текста из файла PDF с использованием PDFMiner в python?

Я был бы рад, если вы хотите повторить мой эксперимент. Вот описание:

1) Запустите упомянутые в этом вопросе коды с любым PDF, который никогда не был зашифрован.

2) Сделайте то же самое с PDF "Безопасный »(это термин, который использует Adobe), я называю его зашифрованным PDF. Используйте общую форму, которую вы можете найти с помощью Google. После того, как вы загрузите его, вам необходимо заполнить поля. В противном случае вы будете проверять наличие меток, а не полей. Данные в полях.

3) Расшифруйте зашифрованный PDF с помощью Pykepdf. Это будет дешифрованный PDF.

4) Снова запустите коды, используя расшифрованный PDF.

ОБНОВЛЕНИЕ 04/4/2019 Камелот (версия на июль 2019 года)

Я нашел библиотеку Python Camelot. Будьте внимательны, если вам нужен camelot-py 0.7.3.

Он очень мощный и работает с Python 3.7. Кроме того, он очень прост в использовании. Во-первых, вам также необходимо установить Ghostscript . Иначе это не сработает. Вам также необходимо установить Pandas . Не использовать pip install camelot-py . Вместо этого используйте pip install camelot-py [cv]

Автор программы - Винаяк Мехта. Франк Ду делится этим кодом в видеоролике YouTube «Извлечение табличных данных из PDF с помощью Camelot Using Python».

Я проверил код, и он работает с незашифрованными файлами. Однако он не работает с зашифрованными и расшифрованными файлами, и это моя цель .

Камелот ориентирован на получение таблиц из PDF-файлов.

Вот код:

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)

ОБНОВЛЕНИЕ 10/7/2019 Я нашел один трюк. Если я открою защищенный PDF-файл с помощью Adobe Reader и распечатаю его с помощью Microsoft в PDF и сохраню как PDF-файл, я смогу извлечь данные, используя эту копию. Я также могу конвертировать PDF-файл в JSON, Excel, SQLite, CSV, HTML и другие форматы. Это возможное решение моего вопроса. Тем не менее, я все еще ищу вариант сделать это без этого трюка, потому что цель состоит в том, чтобы сделать это на 100% с Python. Я также обеспокоен тем, что, если использовать лучший метод шифрования, трюк может бытьне работа. Иногда вам нужно использовать Adobe Reader несколько раз, чтобы получить извлекаемую копию.

ОБНОВЛЕНИЕ 10/8/2019. Третий вопрос. Теперь у меня третий вопрос. Все ли защищенные / зашифрованные PDF защищены паролем? Почему pikepdf не работает? Я предполагаю, что текущая версия pikepdf может нарушить некоторые виды шифрования, но не все из них. @constt упомянул, что PyPDF2 может сломать некоторый тип защиты. Однако я ответил ему, что нашел статью о том, что PyPDF2 может взломать шифрование, выполненное с помощью Adobe Acrobat Pro 6.0, но не с помощью задних версий.

Ответы [ 2 ]

6 голосов
/ 09 октября 2019

ПОСЛЕДНИЕ ОБНОВЛЕННЫЕ 10-11-2019

Я не уверен, полностью ли я понимаю ваш вопрос. Приведенный ниже код можно уточнить, но он читает либо в зашифрованном или незашифрованном формате PDF и извлекает текст. Пожалуйста, дайте мне знать, если я неправильно понял ваши требования.

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)

Я заметил, что в вашем коде pikepdf , используемом для открытия зашифрованного PDF, отсутствует пароль, который должен был выдать это сообщение об ошибке:

pikepdf._qpdf.PasswordError: encrypted.pdf: неверный пароль

import pikepdf

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

Вы можете использовать tika для извлечения текста из расшифрованного.pdf, созданный pikepdf .

from tika import parser

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

Кроме того, pikepdf в настоящее время не поддерживает извлечение текста , включая последнюю версию v1.6.4.


Я решил провести пару тестов с использованием различных зашифрованных файлов PDF.

Я назвал все зашифрованные файлы «encrypted.pdf»и все они использовали один и тот же пароль шифрования и дешифрования.

  1. Adobe Acrobat 9.0 и более поздних версий - 256-битный уровень шифрования AES

    • pikepdf был в состояниичтобы расшифровать этот файл
    • PyPDF2 не удалось извлечьтекст правильно
    • Тика может правильно извлечь текст
  2. Adobe Acrobat 6.0 и более поздние версии - уровень шифрования 128-битный RC4

    • pikepdf смог расшифровать этот файл
    • PyPDF2 не может правильно извлечь текст
    • tika может правильно извлечь текст
  3. Adobe Acrobat 3.0и позже - уровень шифрования 40-битный RC4

    • pikepdf был в состоянии расшифровать этот файл
    • PyPDF2 не может правильно извлечь текст
    • tika может правильно извлечь текст
  4. Adobe Acrobat 5.0 и более поздних версий - 128-битный уровень шифрования RC4

    • , созданный с помощью Microsoft Word
    • pikepdf смограсшифровать этот файл
    • PyPDF2 может правильно извлечь текст
    • Тика может правильно извлечь текст
  5. Adobe Acrobat 9.0 и более поздние версии - уровень шифрования256-битный AES

    • , созданный с использованием pdfprotectfree
    • pikepdf смог расшифровать этот файл
    • PyPDF2 мог правильно извлечь текст
    • Тика мог правильно извлечь текст

PyPDF2 смог извлечь текст из расшифрованных файлов PDF, не созданных с помощью Adobe Acrobat.

Я бы предположил, что сбои связаны со встроенным форматированием PDF-файлов, созданных Adobe Acrobat. Для подтверждения этой гипотезы о форматировании требуется дополнительное тестирование.

Тика смогла извлечь текст из всех документов, расшифрованных с помощью 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 не может расшифровать файлы Acrobat PDF=> 6.0

Эта проблема была открыта для владельцев модулей с 15 сентября 2015 . В комментариях к этой проблеме неясно, когда эта проблема будет решена владельцами проекта. Последний коммит был 25 июня 2018 года.

PyPDF4 проблемы с расшифровкой

PyPDF4 является заменой PyPDF2. Этот модуль также имеет проблемы с расшифровкой с некоторыми алгоритмами, используемыми для шифрования файлов PDF.

тестовый файл: Adobe Acrobat 9.0 и выше - уровень шифрования 256-битный AES

Сообщение об ошибке PyPDF2: поддерживаются только коды алгоритмов 1 и 2

Сообщение об ошибке PyPDF4: толькоалгоритм кода 1 и 2 поддерживаются. В этом PDF-файле используется код 5


ОБНОВЛЕНИЕ РАЗДЕЛА 10-11-2019

Этот раздел является ответом на ваши обновления 10-07-2019 и 10-08-2019.

В своем обновлении вы указали, что можете открыть «защищенный PDF-файл с помощью Adobe Reader» и распечатать документ в другом PDF-файле, в котором отсутствует флаг «ЗАЩИЩЕНО». После некоторого тестирования, я считаю, что выяснили, что происходит в этом сценарии.

Уровень безопасности Adobe PDF

В Adobe PDF есть несколько типов элементов управления безопасностью, которые могут быть включены владельцем документа. Элементы управления могут быть применены либо с помощью пароля, либо с помощью сертификата.

  1. Шифрование документа (обеспечивается с помощью открытого пароля документа)

    • Шифрование всего содержимого документа (большинствообщие)
    • Шифрование всего содержимого документа, кроме метаданных => Acrobat 6.0
    • Шифрование только файловых вложений => Acrobat 7.0
  2. Ограничительное редактирование ипечать (принудительно с паролем разрешений)

    • печать разрешена
    • изменения разрешены

На изображении ниже показан файл Adobe PDFзашифрованы с помощью 256-битного шифрования AES. Чтобы открыть или распечатать этот PDF-файл, необходим пароль. Когда вы открываете этот документ в Adobe Reader с паролем, в заголовке будет указано ЗАЩИЩЕНО

password_level_encryption

Этот документ требует пароль дляоткрыть с помощью модулей Python, упомянутых в этом ответе. Если вы пытаетесь открыть зашифрованный PDF с помощью Adobe Reader. Вы должны увидеть это:

password_prompt

Если вы не получите это предупреждение, то документ либо не имеет включенных элементов управления безопасностью, либо имеет только ограничительное редактирование ипечать включена.

На изображении ниже показано ограничение редактирования, включаемое с паролем в документе PDF. Включена печать заметок . Чтобы открыть или распечатать этот PDF-файл, пароль не требуется . Когда вы открываете этот документ в Adobe Reader без пароля, в заголовке будет указано SECURED Это то же предупреждение, что и зашифрованный PDF-файл, открытый с помощью пароля.

Когда вы распечатываете этот документ в новом PDF, предупреждение SECURED удаляется, так как было удалено ограничительное редактирование.

password_level_restrictive_editing

Все продукты Adobe применяют ограничения, установленные паролем разрешений. Однако если сторонние продукты не поддерживают эти параметры, получатели документа могут обойти некоторые или все установленные ограничения .

Поэтому я предполагаю, что документ, который выпри печати в PDF включено ограничительное редактирование, а не имеет пароля, необходимого для открытия.

Относительно нарушения шифрования PDF

Ни PyPDF2 , ни PyPDF4 не предназначены для взлома функции открытого пароля документадокумента PDF. Оба модуля выдают следующую ошибку, если они попытаются открыть зашифрованный PDF-файл, защищенный паролем.

PyPDF2.utils.PdfReadError: файл не был расшифрован

Функция открытия пароля в зашифрованном PDF-файле может быть обойдена различными способами, но один метод может не работать, а некоторые не будут приемлемы из-за нескольких факторов, включая сложность пароля.

Внутреннее шифрование PDF работает с ключами шифрования 40, 128 или 256 бит в зависимости от версии PDF. Бинарный ключ шифрования получен из пароля, предоставленного пользователем. Пароль зависит от длины и ограничений кодирования.

Например, PDF 1.7 Adobe Extension Level 3 (Acrobat 9 - AES-256) ввел символы Unicode (65 536 возможных символов) и увеличил максимальную длину до 127 байт в представлении UTF-8 пароля.


Приведенный ниже код откроет PDF с включенным ограничительным редактированием. Он сохранит этот файл в новом PDF-файле без добавления предупреждения SECURED. Код tika проанализирует содержимое нового файла.

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)

Этот код проверяет, требуется ли пароль для открытия файла. Этот код будет доработан и могут быть добавлены другие функции. Есть несколько других функций, которые можно добавить, но документация для pikepdf не соответствует комментариям в базе кода, поэтому для ее улучшения требуются дополнительные исследования.

# 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)
1 голос
/ 10 октября 2019

Вы можете попытаться обработать ошибку, которую эти файлы вызывают, когда открываете эти файлы без пароля.

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

Вы можете использовать возвращенный pdf_obj для своей работы по синтаксическому анализу. Кроме того, вы можете указать пароль на случай, если у вас есть зашифрованный PDF.

...