Извлечение данных Python из зашифрованного PDF

12

Я недавний выпускник по чистой математике, который только взял несколько базовых курсов по программированию. Я прохожу стажировку и у меня есть проект по анализу внутренних данных. Я должен проанализировать внутренние 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 Notebooks и 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()

С 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.

ОБНОВЛЕНИЕ 4/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 несколько раз, чтобы получить извлекаемую копию.

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

начинающий
источник
2
Я не мог воспроизвести эти проблемы PyPDF2, все работает просто отлично. Я использовал, pdftkа также онлайн-сервисы для шифрования файлов. Можете ли вы публиковать ссылки на "проблемные" PDF-файлы?
Constt
1
Хорошо, спасибо! Вы пытались использовать qpdfдля расшифровки ваших файлов? В случае, если это сработает, вы можете вызвать его из своего скрипта, используя subprocessмодуль для расшифровки файлов перед их анализом.
constt
1
Во-первых, PyPDF2 не может расшифровать файлы Acrobat PDF => 6.0. Во-вторых, в pikepdf в настоящее время нет функции извлечения текста.
Жизнь сложна
1
@ Begner Я бы предположил, что это связано с базовым форматированием, используемым pykepdf для записи незашифрованного PDF.
Жизнь сложна
2
"Все ли защищенные / зашифрованные PDF защищены паролем?" - нет Также существуют PDF-файлы, зашифрованные с использованием криптографии с закрытым / открытым ключом на основе сертификатов X509.
MKL

Ответы:

8

ПОСЛЕДНИЕ ОБНОВЛЕННЫЕ 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 для извлечения текста из файла decrypted.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 не может правильно извлечь текст
    • Тика может правильно извлечь текст
  3. Adobe Acrobat 3.0 и выше - уровень шифрования 40-битный RC4

    • pikepdf смог расшифровать этот файл
    • PyPDF2 не может правильно извлечь текст
    • Тика может правильно извлечь текст
  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 с паролем, в заголовке будет указано SECURED

password_level_encryption

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

password_prompt

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

На рисунке ниже показано, что ограниченное редактирование разрешено с помощью пароля в документе PDF. Печать заметок включена . Чтобы открыть или распечатать этот PDF-файл, пароль не требуется . Когда вы открываете этот документ в Adobe Reader без пароля, в заголовке будет указано ЗАЩИЩЕНО. Это то же самое предупреждение, что и в зашифрованном 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. Код Тика будет анализировать содержимое из нового файла.

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)
Жизнь сложна
источник
2
Как вы открываете защищенный файл PDF без ввода пароля?
Жизнь сложна
1
Вы имеете в виду только ограничительную защиту редактирования?
Жизнь сложна
1
Ответ обновлен кодом, работающим с PDF, в котором включена ограничительная защита редактирования, но разрешена печать.
Жизнь сложна
1
Вы можете использовать XHTML?
Жизнь сложна
1
Я изменил ответ для вывода XHTML. JSON возможен, но он требует копаться в коде проекта github, связанном с анализатором tika.
Жизнь сложна
1

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

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.

Махендра Сингх
источник
1
Спасибо за ваш ответ! Мы пытаемся прочитать это без пароля. На этот раз мы смогли сделать это с помощью метода, который был описан в моем ОБНОВЛЕНИИ 10/7/2019
новичок
Это далеко не ответ на вопрос. Похоже, вы не прочитали полный вопрос.
shoonya ек
1
Это обрабатывает те защищенные PDF-файлы, где обычно pikepdf завершается ошибкой, когда значением по умолчанию для пароля является None. Пропустив пустую строку, он может правильно открыть и проанализировать защищенный PDF-документ (в тестовых примерах, которые я запускал).
Махендра Сингх
1
@ В этом случае вам не нужно конвертировать PDF-файлы здесь. Это только из моего предыдущего опыта, что безопасные PDF-файлы работают, предоставляя пустой пароль.
Махендра Сингх
1
@ Начинающий это весь мой код. Это только возвращает pdf_object из pikepdf. Если вы хотите сохранить этот PDF-файл, просто сохраните возвращенный объект с помощью pdf_obj.save ('your_file_path'). После этого вы можете использовать этот PDF для анализа текста и других объектов. Я использую библиотеку под названием PdfPlumber для извлечения текста.
Махендра Сингх
1

Для tabula-py вы можете попробовать опцию пароля с read_pdf. Это зависит от функции tabula-java, поэтому я не уверен, какое шифрование поддерживается.

chezou
источник