暗号化されたPDFからのPythonデータの抽出


12

私は純粋な数学の最近の卒業生で、基本的なプログラミングコースをほとんど履修していません。私はインターンシップを行っており、内部データ分析プロジェクトがあります。ここ数年の内部PDFを分析する必要があります。PDFは「保護されています」。つまり、暗号化されます。PDFパスワードはありません。さらに、パスワードが存在するかどうかは不明です。しかし、これらのドキュメントはすべて揃っており、手動で読むことができます。印刷することもできます。目標は、私たちがいくつかのアイデアを持っている言語であるPythonで読むことです。

まず、いくつかのPythonライブラリでPDFを読み込もうとしました。しかし、私が見つけたPythonライブラリは暗号化されたPDFを読みません。当時は、Adobe Readerでもエクスポートできませんでした。

次に、PDFを復号化することにしました。Pythonライブラリーpykepdfを使用して成功しました。Pykepdfは非常にうまく機能します!ただし、復号化されたPDFは、前のポイントのPythonライブラリ(PyPDF2およびTabula)でも読み取ることができません。現時点では、Adobe Readerを使用して復号化されたPDFから情報をエクスポートできるため、多少の改善がありましたが、目的はすべてをPythonで行うことです。

私が示しているコードは、暗号化されていないPDFでは完全に機能しますが、暗号化されたPDFでは機能しません。pykepdfで取得した復号化されたPDFでも機能しません。

私はコードを書きませんでした。PythonライブラリPykepdfTabulaのドキュメントで見つけました。PyPDF2ソリューションは、Al Sweigartの著書「Automate the Boring Stuff with Python」で私が強く推奨しています。また、前に説明した制限付きで、コードが正常に機能していることも確認しました。

最初の質問、プログラムが暗号化されたことのないファイルで動作する場合、なぜ復号化されたファイルを読み取れないのですか?

2番目の質問、復号化されたファイルを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」のみが表示されます

UPDATE 10/3/2019 Pdfminer.six(バージョン2018年11月)

DuckPuncherによって投稿されたソリューションを使用して、より良い結果を得ました。復号化されたファイルの場合、ラベルは取得しましたが、データは取得しませんでした。暗号化されたファイルでも同じことが起こります。暗号化されていないファイルは完璧に機能します。暗号化または復号化されたファイルのデータとラベルが必要なので、このコードは機能しません。その分析には、2018年11月にリリースされたPythonライブラリであるpdfminer.sixを使用しました。Pdfminer.sixには、pycryptodomeライブラリが含まれています。彼らのドキュメントによると「PyCryptodomeは低レベルの暗号化プリミティブの自己完結型Pythonパッケージです。」

コードはスタック交換の質問にあります: PythonでPDFMinerを使用してPDFファイルからテキストを抽出しますか?

私の実験を繰り返したいと思います。ここに説明があります:

1)暗号化されていないPDFを使用して、この質問で言及されているコードを実行します。

2)PDF "Secure"(これはAdobeが使用する用語です)でも同じことを行います。私はそれを暗号化PDFと呼んでいます。Googleを使用して見つけることができる一般的なフォームを使用します。ダウンロードした後、フィールドに入力する必要があります。それ以外の場合は、ラベルではなくフィールドをチェックします。データはフィールドにあります。

3)Pykepdfを使用して暗号化されたPDFを復号化します。これが復号化されたPDFになります。

4)復号化されたPDFを使用してコードを再度実行します。

UPDATE 10/4/2019 Camelot(バージョンJuly 2019)

PythonライブラリCamelotを見つけました。camelot-py 0.7.3が必要になることに注意してください

これは非常に強力で、Python 3.7で動作します。また、非常に使いやすいです。まず、Ghostscriptもインストールする必要があります。それ以外の場合は機能しません。Pandasもインストールする必要があります。 pip install camelot-pyは使用しないでください。代わりにpip install camelot-py [cv]を使用してください

プログラムの作成者はVinayak Mehtaです。Frank DuがこのコードをYouTubeビデオ「Pythonを使用したCamelotでPDFから表形式のデータを抽出する」で共有しています。

コードを確認したところ、暗号化されていないファイルで動作しています。ただし、暗号化および復号化されたファイルでは機能しませんそれが私の目標です。

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)

更新10/7/2019 1つのトリックを見つけました。保護されたPDFをAdobe Readerで開き、Microsoftを使用してPDFに印刷し、PDFとして保存すると、そのコピーを使用してデータを抽出できます。PDFファイルをJSON、Excel、SQLite、CSV、HTML、その他の形式に変換することもできます。これは私の質問に対する可能な解決策です。ただし、目標はPythonで100%実行することなので、そのトリックなしでそれを実行するオプションをまだ探しています。また、暗号化のより良い方法が使用された場合、トリックが機能しない可能性があることも心配しています。抽出可能なコピーを取得するために、Adobe Readerを数回使用する必要がある場合があります。

2019年10月8日更新。3番目の質問。 3つ目の質問です。保護/暗号化された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
@Beginnerこれは、暗号化されていないPDFを書き込むためにpykepdfによって使用されている基本的なフォーマットに関係していると推測します。
人生は複雑です

2
「すべての保護された/暗号化されたpdfはパスワードで保護されていますか?」- 番号。X509証明書に基づく秘密/公開キー暗号化を使用して暗号化されたPDFもあります。
mkl

回答:


8

最終更新日2019年10月11日

あなたの質問を完全に理解したかどうかわかりません。以下のコードは改良できますが、暗号化されたPDFまたは暗号化されていない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")

あなたは使用することができますティカをによって作成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はテキストを正しく抽出できませんでした
    • tikaはテキストを正しく抽出できました
  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はテキストを正しく抽出できます
    • tikaはテキストを正しく抽出できました
  5. Adobe Acrobat 9.0以降-暗号化レベル256ビットAES

    • pdfprotectfreeを使用して作成
    • pikepdfはこのファイルを復号化できました
    • PyPDF2はテキストを正しく抽出できます
    • tikaはテキストを正しく抽出できました

PyPDF2は、Adobe Acrobatで作成されていない復号化されたPDFファイルからテキストを抽出できました。

この失敗は、Adobe Acrobatで作成されたPDFに埋め込まれたフォーマットに関係していると思います。フォーマットに関するこの推測を確認するには、さらにテストが必要です。

tikaは、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

この問題は、2015年9月15日以降、モジュールの所有者に公開されています。この問題がプロジェクトオーナーによっていつ修正されるかは、この問題に関連するコメントでは不明です。最後のコミットは2018年6月25日でした。

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の更新に対応しています。

更新では、「Adobe Readerで保護されたPDF」を開き、そのドキュメントを別のPDFに印刷して、「SECURED」フラグを削除できると述べました。いくつかのテストを行った後、私はこのシナリオで何が起こっているのかを理解したと信じています。

セキュリティの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で開くと、タイトルにSECURED と表示されます。これは、パスワードで開かれた暗号化されたPDFと同じ警告です。

このドキュメントを新しいPDFに印刷すると、制限付きの編集が削除されたため、SECURED警告が削除されます。

password_level_restrictive_editing

すべてのアドビ製品は、権限パスワードによって設定された制限を適用します。ただし、サードパーティ製品がこれらの設定をサポートしていない場合、ドキュメントの受信者は設定た制限の一部またはすべてをバイパスできます。

したがって、PDFに印刷するドキュメントでは制限付き編集が有効になっており、開くために必要なパスワードが有効になっていないと思います。

PDF暗号化の解除について

どちらPyPDF2またはPyPDF4は、 PDF文書の文書オープンパスワード機能を破るために設計されています。暗号化されたパスワードで保護されたPDFファイルを開こうとすると、両方のモジュールで次のエラーがスローされます。

PyPDF2.utils.PdfReadError:ファイルが復号化されていません

暗号化されたPDFファイルのパスワードを開く機能は、さまざまな方法を使用して迂回できますが、単一の手法では機能せず、パスワードの複雑さなどのいくつかの要因により、一部の手法が受け入れられない場合があります。

PDF暗号化は、PDFバージョンに応じて、40、128、または256ビットの暗号化キーで内部的に機能します。バイナリ暗号化キーは、ユーザーが提供したパスワードから取得されます。パスワードは、長さとエンコードの制約を受けます。

たとえば、PDF 1.7 Adob​​e Extension Level 3(Acrobat 9-AES-256)はUnicode文字(65,536文字)を導入し、パスワードのUTF-8表現で最大長を127バイトに増やしました。


以下のコードは、制限付き編集が有効になっているPDFを開きます。SECURED警告が追加されることなく、このファイルが新しいPDFに保存されます。ティカのコードは、新しいファイルの内容を解析します。

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は可能ですが、tikaパーサーに関連する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
回答ありがとうございます!パスワードなしで読もうとしています。この時、私たちは私のUPDATE 2019年10月7日に説明した方法でそれを行うことができました
初心者の

これは質問に答えるのにはほど遠い。質問全体を読んでいないようです。
shoonya ek

1
これは、パスワードのデフォルト値がNoneのときに通常pikepdfが失敗する保護されたPDFを処理します。空の文字列を渡すことで、保護されたPDFドキュメントを適切に開いて解析できます(私が実行したテストケース)。
Mahendra Singh

1
@Beginnerこの場合、PDFをここで変換する必要はありません。これは、保護されたPDFが空のパスワードを提供することで機能するという私の以前の経験からのものです。
Mahendra Singh

1
@Beginnerこれは私のコード全体です。これは、pikepdfからpdf_objectのみを返します。このpdfを保存する場合は、pdf_obj.save( 'your_file_path')を使用して、返されたオブジェクトを保存します。この後、このPDFを使用してテキストやその他のオブジェクトを解析できます。テキストの抽出にはPdfPlumberというライブラリを使用しています。
Mahendra Singh

1

tabula-pyの場合、read_pdfでパスワードオプションを試すことができます。これはtabula-javaの機能に依存するため、サポートされている暗号化はわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.