Витяг даних Python із зашифрованого PDF


12

Я недавній випускник з чистої математики, який пройшов лише кілька базових курсів програмування. Я проходжу стажування та маю проект внутрішнього аналізу даних. Мені доводиться аналізувати внутрішні PDF-файли останніх років. PDF-файли "захищені". Іншими словами, вони зашифровані. У нас немає паролів PDF, навіть більше, ми не впевнені, чи існують паролі. Але, у нас є всі ці документи, і ми можемо їх читати вручну. Ми також можемо їх надрукувати. Мета полягає в тому, щоб прочитати їх з Python, оскільки мова, якою ми маємо певне уявлення.

По-перше, я спробував прочитати PDF-файли з деякими бібліотеками Python. Однак бібліотеки Python, які я знайшов, не читають зашифровані PDF-файли. У той час я не міг експортувати інформацію за допомогою Adobe Reader.

По-друге, я вирішив розшифрувати PDF-файли. Мені вдалося використовувати pykepdf бібліотеки Python. 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, ноутбуків Юпітера та 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.10.2019 Pdfminer.six (версія листопада 2018 р.)

Я отримав кращі результати, використовуючи рішення, розміщене DuckPuncher . Для розшифрованого файлу я отримав мітки, але не дані. Те саме відбувається з зашифрованим файлом. Для файлу, який ніколи не був зашифрований, працює ідеально. Оскільки мені потрібні дані та мітки зашифрованих чи розшифрованих файлів, цей код для мене не працює. Для цього аналізу я використав pdfminer.six, що є бібліотекою Python, яка була випущена в листопаді 2018 року. Pdfminer.six включає бібліотечний пікриптодом. Згідно з їх документацією, " PyCryptodome - це автономний пакет Python криптографічних примітивів низького рівня."

Код у питанні обміну стеками : Вилучення тексту з PDF-файлу за допомогою PDFMiner в python?

Я хотів би, якщо ви хочете повторити мій експеримент. Ось опис:

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

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

3) Розшифруйте зашифрований PDF за допомогою Pykepdf. Це буде розшифрований PDF.

4) Запустіть коди знову, використовуючи розшифрований PDF.

ОНОВЛЕННЯ 4.10.2019 Camelot (версія липня 2019)

Я знайшов бібліотеку Python Camelot. Будьте уважні, що вам знадобиться камелот-пі 0.7.3.

Він дуже потужний і працює з Python 3.7. Крім того, він дуже простий у використанні. По-перше, вам також потрібно встановити Ghostscript . Інакше це не вийде. Вам також потрібно встановити Pandas . Не використовуйте pip install camelot-py . Замість цього використовуйте pip install camelot-py [cv]

Автор програми - Віняк Мехта. Франк Ду ділиться цим кодом у відео на YouTube "Витяг табличних даних з PDF із Camelot Using Python".

Я перевірив код і він працює з незашифрованими файлами. Однак це не працює із зашифрованими та розшифрованими файлами, і це моя мета .

Camelot орієнтований на отримання таблиць з 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)

ОНОВЛЕННЯ 7.10.2019 Я знайшов одну хитрість. Якщо я відкрию захищений pdf за допомогою Adobe Reader і друкую його за допомогою Microsoft у PDF, і зберігаю його як PDF, я можу отримати дані за допомогою цієї копії. Я також можу конвертувати PDF-файл у формати JSON, Excel, SQLite, CSV, HTML та інші формати. Це можливе рішення мого питання. Однак я все ще шукаю варіант зробити це без цього фокусу, оскільки мета - це зробити 100% за допомогою Python. Я також стурбований тим, що якщо використовувати кращий метод шифрування, трюк, можливо, не спрацює. Іноді потрібно отримати Adobe Reader кілька разів, щоб отримати копію, яку можна отримати.

ОНОВЛЕННЯ 8.10.2019. Третє питання. У мене зараз третє питання. Чи всі захищені / зашифровані PDF захищені паролем? Чому pikepdf не працює? Думаю, що поточна версія pikepdf може порушити певний тип шифрування, але не всі. @constt зазначав, що PyPDF2 може зламати певний тип захисту. Однак я відповів йому, що знайшов статтю про те, що PyPDF2 може порушувати шифрування, зроблені за допомогою Adobe Acrobat Pro 6.0, але ні із задніми версіями.


2
Я не міг відтворити ці проблеми PyPDF2, все працює просто чудово. Я також використовував pdftkонлайн-сервіси для шифрування файлів. Чи можете ви розміщувати посилання на "проблемні" файли PDF?
constt

1
Добре, дякую! Ви намагалися використовувати qpdfдля розшифрування файлів? У випадку, якщо це зробить трюк, ви можете викликати його зі свого скрипту за допомогою subprocessмодуля, щоб розшифрувати файли, перш ніж їх розбирати.
constt

1
По-перше, PyPDF2 не може розшифрувати PDF-файли Acrobat => 6.0. По-друге, pikepdf в даний час не має програми для вилучення тексту.
Життя є складним

1
@Beginner Я б припускав, що це пов'язане з основним форматуванням, яке використовується pykepdf для написання незашифрованого PDF.
Життя є складним

2
"Чи всі захищені / зашифровані PDF захищені паролем?" - ні. Також є pdfs, зашифровані за допомогою криптографії приватного / відкритого ключа на основі сертифікатів 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")

Ви можете використовувати тику для отримання тексту з розшифрованого файлу .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 не може розшифрувати PDF-файли Acrobat => 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 без пароля, заголовок буде вказано ЗАБЕЗПЕЧЕНО Це те саме попередження, що і зашифрований PDF-файл, відкритий паролем.

Коли ви друкуєте цей документ у новому PDF- файлі, ЗАБЕЗПЕЧЕНО попередження видаляється, оскільки обмежувальне редагування було видалено.

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-файлі без додавання ЗАБЕЗПЕЧЕНОГО попередження. Код 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)

2
Як ви відкриваєте захищений файл PDF без надання пароля?
Життя складна

1
Ви маєте на увазі лише обмежувальний захист від редагування?
Життя є складним

1
Відповідь оновлено кодом, який працював із PDF, у якому включений обмежувальний захист від редагування, але друк дозволявся.
Життя є складним

1
Чи можете ви використовувати XHTML?
Життя є складним

1
Я змінив відповідь на вихід XHTML. JSON можливий, але він потребує кодування до коду проекту github, пов'язаного з тикером парсера.
Життя є складним

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
Спасибі за вашу відповідь! Ми намагаємося прочитати його без пароля. На цей час нам вдалося це зробити методом, який був пояснений у моєму ОНОВЛЕННІ 7.10.2019
початківців

Це далеко не відповідь на запитання. Здається, ви не прочитали повного запитання.
shoonya ek

1
Це обробляє ті захищені PDF-файли, коли звичайно файл pikepdf виходить з ладу, коли значення пароля за замовчуванням є None. Пройшовши порожній рядок, він зможе належним чином відкрити та проаналізувати захищений документ PDF (у тестових випадках, які я запускав).
Махендра Сінгх

1
@Beginner u в цьому випадку не доведеться конвертувати PDF-файли. Це лише з мого попереднього досвіду, що захищені PDF-файли працюють, надаючи порожній пароль.
Махендра Сінгх

1
@Beginner - це весь мій код. Це повертає лише pdf_object з pikepdf. Якщо ви хочете зберегти цей pdf, просто збережіть повернутий об’єкт, використовуючи pdf_obj.save ('your_file_path'). Після цього ви можете використовувати цей PDF для розбору тексту та інших об'єктів. Я використовую бібліотеку під назвою PdfPlumber для вилучення тексту.
Махендра Сінгх

1

Для tabula-py можна спробувати параметр пароля з read_pdf. Це залежить від функції tabula-java, тому я не впевнений, яке шифрування підтримується.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.