PDFをPython(PyPDF2)で操作する - PDF・暗号化PDFファイルの読み込み

今回はPDFファイルをPythonで操作する方法を紹介したいと思います。
事前準備
まずは事前準備を行いましょう。 なお、実行環境との依存モジュールのバージョンは以下として話を進めます。
- python 3.6
- PyPDF2 1.26.0
PyPDF2のインストール
PythonでPDFファイルの操作をする時には PyPDF2 を使います。
PyPDF2 は
- PDFファイルからの情報の抽出
- 既存のPDFを操作し、新しいファイルを生成する
を得意としています。
それでは、 pip コマンドでモジュールをインストールしましょう。
1pip install PyPDF2PDFファイルの準備
読み込めそうなPDFファイルを入手しましょう。
今回は アメリカ大統領からの大統領令のページ から任意の大統領令のPDFファイルをダウンロードします。
見た目はこんな感じです。全部で3ページほどありました。

PDFファイルを読み込む
ファイル読み込みを早速やってみましょう。 以下のサンプルコードでは、PDFファイル内のページ数を標準出力に表示します。
1import PyPDF2
2
3FILE_PATH = './files/executive_order.pdf'
4
5with open(FILE_PATH, mode='rb') as f:
6 reader = PyPDF2.PdfFileReader(f)
7 print(f"Number of pages: {reader.getNumPages()}")まずは、インストールした PyPDF2 モジュールをインポートします。
その後、PDFファイルを バイナリの読み込みモード で開きます。
開いたファイルオブジェクトから、PDFを取り扱うための PdfFileReader オブジェクトを作成します。
問題なく、以下のように表示されました。
Number of pages: 3パスワード付きPDFファイル(暗号化PDFファイル)を読み込む
次にパスワード付きのPDFファイルを読み込みます。 PDF保存時の 暗号化オプション にて、任意のパスワードを設定したファイルのことを指します。
失敗パターン
先程の大統領令のPDFファイルにパスワード(hoge1234)を付けて保存したものを executive_order_encrypted.pdf とし、
パスワード なし の時に読み込んだコードで実行してみます。
1# PDFの読み込みに失敗するコード
2import PyPDF2
3
4FILE_PATH = './files/executive_order_encrypted.pdf'
5
6with open(FILE_PATH, mode='rb') as f:
7 reader = PyPDF2.PdfFileReader(f)
8 print(f"Number of pages: {reader.getNumPages()}")復号化が必要性が以下のようなエラーメッセージから判断できます。
PdfReadError: File has not been decrypted暗号化PDFファイルを復号化する
ここで言う「復号化」は パスワードを解除すること を意味しています。
decrypt 関数で復号化を行います。引数にはパスワード文字列を渡します。
なお、 decrypt 関数を呼び出す前に isEncrypted を呼び出し、
ファイルが暗号化されているか否かをチェックした方が親切でしょう。
1import PyPDF2
2
3ENCRYPTED_FILE_PATH = './files/executive_order_encrypted.pdf'
4
5with open(ENCRYPTED_FILE_PATH, mode='rb') as f:
6 reader = PyPDF2.PdfFileReader(f)
7 if reader.isEncrypted:
8 reader.decrypt('hoge1234')
9 print(f"Number of page: {reader.getNumPages()}")Number of pages: 3decrypt 時に NotImplementedError が表示される場合の対処法
暗号化PDFを扱う際、以下のエラーに遭遇するかもしれません。
NotImplementedError: only algorithm code 1 and 2 are supportedこれは、PyPDF2 が復号化するためのロジックを実装していないことが原因で発生するエラーです。こうなっては PyPDF2 単体で解決するのは難しくなります。
Pythonコードから qpdf をキックして復号化する
いくつか調べてわかったのですが、てっとり早いのは qpdf を組み合わせて使う方法でした。 qpdf はPDFの操作をCLI上から行うためのツールです。
Windows版であれば SourceForge からインストーラを使い、Macであれば brew install qpdf で使えるようになります。
話を戻すと、NotImplementedError の原因は、復号化の処理が PyPDF2 に実装されていないことだったので、復号化の処理のみを qpdf で行えばよいのです。
1import PyPDF2
2import os
3
4ENCRYPTED_FILE_PATH = './files/executive_order_encrypted.pdf'
5FILE_OUT_PATH = './files/executive_order_out.pdf'
6
7PASSWORD='hoge1234'
8
9with open(ENCRYPTED_FILE_PATH, mode='rb') as f:
10 reader = PyPDF2.PdfFileReader(f)
11 if reader.isEncrypted:
12 try:
13 reader.decrypt(PASSWORD)
14 except NotImplementedError:
15 command=f"qpdf --password='{PASSWORD}' --decrypt {ENCRYPTED_FILE_PATH} {FILE_OUT_PATH};"
16 os.system(command)
17 with open(FILE_OUT_PATH, mode='rb') as fp:
18 reader = PyPDF2.PdfFileReader(fp)
19 print(f"Number of page: {reader.getNumPages()}")Pythonコードから qpdf コマンドをOSのコマンドとして実行し、
複合化したPDFファイルをパスワードなしの別ファイルとして保存 します。
その後、もう一度 PdfFileReader にてファイルを読み込ませる、という算段です。
なお、このサンプルコードですと、 with 句によってファイルが自動クローズされてしまうため、実際にはもう少しコードのスコープを整理してあげる方が汎用性の面で良いと思います。
まとめ
今回は PyPDF2 を使ってPDFファイルを開く方法をまとめました。
- ファイル読み込みには
PdfFileReaderを使う - 暗号化PDFには
decrypt関数で復号化する - 復号化時に
NotImplementedErrorが出る場合には、復号化の処理は qpdf で行う
