Pythonライブラリ「pypdf」徹底解説 (旧PyPDF2からの進化) 🐍📄

Python

PDF操作の決定版!読み込み、書き込み、結合、分割、テキスト抽出などをマスターしよう

ビジネス文書やレポート、論文など、様々な場面で利用されるPDF (Portable Document Format)。その利便性の高さから広く普及していますが、プログラムから直接操作するのは少し厄介なイメージがあるかもしれません🤔。しかし、Pythonを使えば、PDFファイルの操作を自動化し、日々の業務や研究を効率化することが可能です!

かつてPythonでPDFを扱うライブラリとして「PyPDF2」が広く知られていましたが、2022年末にバージョン3.0.xを最後に開発が終了し、その開発は元々のプロジェクト名である「pypdf」に引き継がれました。PyPDF2の多くの機能やコンセプトはpypdfに継承されており、現在ではpypdfが推奨されるライブラリとなっています。このブログでは、最新の「pypdf」ライブラリに焦点を当て、その基本的な使い方から応用的なテクニックまで、詳しく解説していきます。

この記事を読めば、以下のことができるようになります✨:

  • pypdfライブラリのインストール
  • PDFファイルの基本的な読み込みと情報取得
  • ページの抽出、結合、分割、回転
  • PDFからのテキスト抽出
  • PDFファイルの暗号化と復号化
  • メタデータの操作

さあ、Pythonとpypdfを使って、PDF操作の世界を探求しましょう!🚀

1. pypdfのインストール

pypdfはPythonのパッケージ管理ツールであるpipを使って簡単にインストールできます。ターミナルやコマンドプロンプトで以下のコマンドを実行してください。

pip install pypdf

もし、AES暗号化方式で保護されたPDFを扱いたい場合は、追加の依存ライブラリも一緒にインストールする必要があります。RC4暗号化方式は上記の通常インストールで対応しています。

pip install pypdf[crypto]

インストールが完了したら、Pythonスクリプトでimport pypdfを実行してエラーが出なければ成功です。

pypdfは純粋なPythonライブラリであり、基本機能に関してはPython標準ライブラリ以外の外部依存関係がないため、多くの環境で手軽に利用開始できるのが魅力です。

2. 基本的な使い方: PDFの読み込みと情報取得

pypdfの基本的な操作は、PdfReaderクラスとPdfWriterクラス(旧PyPDF2ではPdfFileReaderPdfFileWriterでしたが、変更されました)を使って行います。まずはPDFファイルを読み込み、基本的な情報を取得してみましょう。

PdfReaderクラスを使うと、既存のPDFファイルを読み込むことができます。コンストラクタにPDFファイルのパスを指定します。

from pypdf import PdfReader

# PDFファイルを読み込みモード('rb')で開く
try:
    reader = PdfReader("input.pdf")

    # 総ページ数を取得
    num_pages = len(reader.pages)
    print(f"総ページ数: {num_pages}")

    # 1ページ目を取得 (インデックスは0から始まる)
    if num_pages > 0:
        first_page = reader.pages[0]
        print("1ページ目のオブジェクトを取得しました。")
    else:
        print("PDFにページがありません。")

except FileNotFoundError:
    print("エラー: 指定されたPDFファイルが見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

reader.pagesは、PDFの各ページに対応するPageObjectのリストを返します。len()関数でそのリストの長さを調べることで、総ページ数を取得できます。

PDFファイルには、タイトル、作成者、作成日などのメタデータが含まれていることがあります。PdfReaderオブジェクトのmetadata属性を使ってこれらの情報を取得できます。

from pypdf import PdfReader

try:
    reader = PdfReader("input.pdf")
    meta = reader.metadata

    if meta:
        print("--- メタデータ ---")
        print(f"タイトル: {meta.title}")
        print(f"作成者: {meta.author}")
        print(f"サブジェクト: {meta.subject}")
        print(f"作成ツール: {meta.producer}")
        print(f"作成日時: {meta.creation_date}")
        print(f"更新日時: {meta.modification_date}")
        print("------------------")
    else:
        print("メタデータが見つかりませんでした。")

except FileNotFoundError:
    print("エラー: 指定されたPDFファイルが見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

metadata属性はDocumentInformationオブジェクトを返します。このオブジェクトの各属性(.title, .authorなど)にアクセスすることで、対応するメタデータを取得できます。ただし、PDFファイルにメタデータが含まれていない場合はNoneが返ることがあります。

3. PDFの操作: ページの抽出、結合、分割、回転

pypdfを使えば、PDFファイルのページ単位での様々な操作が可能です。ここでは代表的な操作を見ていきましょう。これらの操作では、新しいPDFファイルを作成するためにPdfWriterクラスがよく使われます。

複数のPDFファイルを一つにまとめるには、PdfMergerクラス(旧PyPDF2には同名のクラスがありましたが、pypdfではPdfWriterappend()メソッドを使う方法がより一般的になっています)を利用するか、PdfWriterを直接使う方法があります。ここではPdfWriterを使った例を示します。

from pypdf import PdfWriter, PdfReader

pdf_files = ["file1.pdf", "file2.pdf", "file3.pdf"]
output_filename = "merged_output.pdf"

writer = PdfWriter()

try:
    for pdf_path in pdf_files:
        reader = PdfReader(pdf_path)
        for page in reader.pages:
            writer.add_page(page) # ページを順番に追加

    # 結合した内容を新しいファイルに書き出す
    with open(output_filename, "wb") as output_pdf:
        writer.write(output_pdf)

    print(f"'{output_filename}' にPDFファイルを結合しました。🎉")

except FileNotFoundError as e:
    print(f"エラー: ファイルが見つかりません - {e.filename}")
except Exception as e:
    print(f"エラーが発生しました: {e}")
finally:
    writer.close() # Writerオブジェクトを閉じる

このコードは、pdf_filesリストに含まれるPDFファイルを順番に読み込み、各ページのPageObjectPdfWriterインスタンスに追加していきます。最後にwriter.write()メソッドで指定したファイル名で保存します。

特定のページ範囲だけを結合したい場合は、PdfReaderオブジェクトからページを取得する際にインデックスやスライスを使います。例えば、`file1.pdf`の最初の3ページと`file2.pdf`の全ページを結合する場合などです。

1つのPDFファイルをページごとに別々のファイルに分割したり、特定のページ範囲で分割したりすることも可能です。ここでは、1ページずつ個別のファイルとして保存する例を示します。

from pypdf import PdfReader, PdfWriter
import os

input_filename = "large_document.pdf"
output_dir = "split_pages"

try:
    reader = PdfReader(input_filename)
    num_pages = len(reader.pages)

    # 出力ディレクトリがなければ作成
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    for i in range(num_pages):
        writer = PdfWriter()
        page = reader.pages[i]
        writer.add_page(page)

        # 出力ファイル名 (例: split_pages/page_1.pdf)
        output_filename = os.path.join(output_dir, f"page_{i + 1}.pdf")

        with open(output_filename, "wb") as output_pdf:
            writer.write(output_pdf)
        writer.close() # 各ファイルごとにWriterを閉じる

    print(f"'{input_filename}' を {num_pages} 個のファイルに分割し、'{output_dir}' に保存しました。📂")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

このコードは、入力PDFの各ページに対して新しいPdfWriterを作成し、そのページだけを追加して個別のファイルとして保存します。

特定のページ範囲で分割したい場合は、例えば1から10ページまでを一つのファイル、11ページ以降を別のファイルにする、といった処理をPdfReaderのスライス機能やループを使って実装します。

特定のページや、PDF全体のページを回転させることができます。回転角度は90度の倍数で指定します。

from pypdf import PdfReader, PdfWriter

input_filename = "needs_rotation.pdf"
output_filename = "rotated_output.pdf"
rotation_angle = 90 # 時計回りに90度回転

try:
    reader = PdfReader(input_filename)
    writer = PdfWriter()

    for page in reader.pages:
        # ページを指定した角度で回転させる
        page.rotate(rotation_angle)
        writer.add_page(page)

    with open(output_filename, "wb") as output_pdf:
        writer.write(output_pdf)
    writer.close()

    print(f"'{input_filename}' の全ページを {rotation_angle} 度回転させ、'{output_filename}' に保存しました。🔄")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

page.rotate()メソッドは、PageObjectを直接変更します。正の数は時計回り、負の数は反時計回りの回転を示します。特定のページだけ回転させたい場合は、ループ内で条件分岐(例: if i == target_page_index:)を追加します。

4. PDFからのテキスト抽出

PDFファイルに含まれるテキスト情報を抽出することも、pypdfの重要な機能の一つです。ただし、PDFの構造は複雑であり、特に画像として保存されたテキストや特殊なエンコーディングが使われている場合、完璧な抽出が難しいこともあります。

from pypdf import PdfReader

input_filename = "document_with_text.pdf"

try:
    reader = PdfReader(input_filename)
    print(f"--- '{input_filename}' からテキストを抽出 ---")

    full_text = ""
    for i, page in enumerate(reader.pages):
        try:
            # 各ページからテキストを抽出
            text = page.extract_text()
            if text: # テキストが存在する場合のみ追加
                print(f"\n=== ページ {i + 1} ===")
                print(text)
                full_text += text + "\n" # 全文テキストにも追加
            else:
                print(f"\n=== ページ {i + 1} (テキスト抽出不可) ===")
        except Exception as e_page:
            print(f"\n=== ページ {i + 1} (抽出エラー: {e_page}) ===")


    # 抽出した全文をファイルに保存(オプション)
    # with open("extracted_text.txt", "w", encoding="utf-8") as f:
    #     f.write(full_text)
    # print("\n--- 全文を 'extracted_text.txt' に保存しました ---")

    print("\n--- 抽出完了 ---")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

PageObjectextract_text()メソッドを使うことで、そのページからテキストを抽出できます。上記のコードでは、各ページのテキストをコンソールに出力し、さらにfull_text変数に全文を結合しています。

注意点:

  • スキャンされた画像ベースのPDFからは、テキストを直接抽出できません。このような場合は、OCR(光学文字認識)ライブラリ(例: Tesseractとpytesseract)を別途利用する必要があります。
  • 複雑なレイアウト(多段組、表、図中の文字など)を持つPDFでは、テキストの順序やフォーマットが正確に再現されない場合があります。
  • パスワードで保護されたPDFからは、まず復号化しないとテキスト抽出はできません。

5. PDFの暗号化と復号化

pypdfは、PDFファイルをパスワードで保護(暗号化)したり、パスワードで保護されたPDFを解除(復号化)したりする機能も提供しています。

既存のPDFファイルにパスワードを設定して暗号化するには、PdfWriterencrypt()メソッドを使います。

from pypdf import PdfReader, PdfWriter

input_filename = "confidential_report.pdf"
output_filename = "encrypted_report.pdf"
password = "your_strong_password" # 👈 強力なパスワードを設定してください

try:
    reader = PdfReader(input_filename)
    writer = PdfWriter()

    # 全ページをWriterに追加
    for page in reader.pages:
        writer.add_page(page)

    # パスワードを設定して暗号化 (AES-256がデフォルト)
    # RC4を使いたい場合は algorithm="RC4-128" を指定
    writer.encrypt(password)

    # 暗号化されたPDFを保存
    with open(output_filename, "wb") as output_pdf:
        writer.write(output_pdf)
    writer.close()

    print(f"'{input_filename}' をパスワードで暗号化し、'{output_filename}' に保存しました。🔒")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    # cryptoライブラリがない場合にエラーになる可能性がある
    if "cryptography is required for AES algorithm" in str(e):
         print("エラー: AES暗号化には 'pip install pypdf[crypto]' が必要です。")
    else:
        print(f"エラーが発生しました: {e}")

encrypt()メソッドの第一引数にユーザーパスワード(ファイルを開くためのパスワード)を指定します。必要であれば、第二引数にオーナーパスワード(権限変更用のパスワード)も設定できます。デフォルトではAES暗号化が使用されますが、`[crypto]` 付きでインストールしていない場合はRC4のみ利用可能です。

パスワードで保護されたPDFファイルを読み込むには、PdfReaderを開く際にパスワードを指定するか、後からdecrypt()メソッドを使用します。

from pypdf import PdfReader, PdfWriter

input_filename = "encrypted_report.pdf" # 暗号化されたファイル
output_filename = "decrypted_report.pdf" # 復号化後のファイル名
password = "your_strong_password" # 👈 正しいパスワード

try:
    reader = PdfReader(input_filename)

    # パスワードが設定されているか確認
    if reader.is_encrypted:
        print("PDFは暗号化されています。復号化を試みます...")
        # パスワードを使って復号化
        if reader.decrypt(password):
            print("復号化に成功しました!🔓")

            # 復号化した内容を新しいPDFとして保存(パスワードなし)
            writer = PdfWriter()
            for page in reader.pages:
                writer.add_page(page)

            with open(output_filename, "wb") as output_pdf:
                writer.write(output_pdf)
            writer.close()
            print(f"復号化した内容を '{output_filename}' に保存しました。")

        else:
            # decrypt()メソッドが 0 または PasswordType.OWNER_PASSWORD を返した場合
            print("エラー: パスワードが正しくないか、ファイルの復号化に失敗しました。🔑")
    else:
        print("このPDFファイルは暗号化されていません。")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    # cryptoライブラリがない場合にエラーになる可能性がある
    if "cryptography is required for AES algorithm" in str(e):
         print("エラー: AES復号化には 'pip install pypdf[crypto]' が必要です。")
    else:
        print(f"エラーが発生しました: {e}")

reader.is_encryptedでファイルが暗号化されているか確認できます。reader.decrypt(password)を実行し、成功するとPasswordType.USER_PASSWORD (通常は1)が返され、以降はそのPdfReaderオブジェクトを通じて復号化された内容にアクセスできます。パスワードが違うなどの理由で失敗した場合は0などが返ります。上記の例では、復号化に成功した場合、パスワードが解除された新しいPDFファイルを作成しています。

6. メタデータの変更・削除

PDFファイルのメタデータ(タイトル、作成者など)を更新したり、削除したりすることも可能です。

PdfWriteradd_metadata()メソッドを使って、出力するPDFファイルにメタデータを設定します。

from pypdf import PdfReader, PdfWriter

input_filename = "report_no_meta.pdf"
output_filename = "report_with_meta.pdf"

new_metadata = {
    '/Title': '月次業績報告書',
    '/Author': '営業部',
    '/Subject': '2025年4月度 報告',
    # '/CreationDate': 'D:20250401090000+09\'00\'' # 必要であれば日付も設定可能 (書式注意)
}

try:
    reader = PdfReader(input_filename)
    writer = PdfWriter()

    # 既存のページをコピー
    for page in reader.pages:
        writer.add_page(page)

    # 新しいメタデータを追加(既存のメタデータは上書きされる)
    writer.add_metadata(new_metadata)

    # メタデータ付きでPDFを保存
    with open(output_filename, "wb") as output_pdf:
        writer.write(output_pdf)
    writer.close()

    print(f"'{output_filename}' にメタデータを追加/変更して保存しました。📝")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

add_metadata()には、キーと値のペアを持つ辞書を渡します。キーは/Title, /Authorのようにスラッシュで始まる必要があります。このメソッドは既存のメタデータをすべて置き換えるため、元のメタデータを保持しつつ一部を変更したい場合は、まずreader.metadataで既存のメタデータを取得し、それを更新してからadd_metadata()に渡す必要があります。

PDFからメタデータを完全に削除するには、PdfReaderからPdfWriterへページをコピーする際に、メタデータをコピーしないようにします。add_metadata()を呼び出さなければ、メタデータなしのPDFが作成されます。

from pypdf import PdfReader, PdfWriter

input_filename = "report_with_meta.pdf" # メタデータ付きファイル
output_filename = "report_meta_removed.pdf"

try:
    reader = PdfReader(input_filename)
    writer = PdfWriter()

    # メタデータをコピーせずにページのみをコピー
    writer.clone_document_from_reader(reader) # メタデータを含めずにクローンする簡単な方法

    # # もしくは、ページごとにコピーする場合:
    # for page in reader.pages:
    #     writer.add_page(page)

    # メタデータを設定しないまま保存
    with open(output_filename, "wb") as output_pdf:
        writer.write(output_pdf)
    writer.close()

    print(f"'{output_filename}' からメタデータを削除して保存しました。🗑️")

except FileNotFoundError:
    print(f"エラー: '{input_filename}' が見つかりません。")
except Exception as e:
    print(f"エラーが発生しました: {e}")

writer.clone_document_from_reader(reader) を使うと、メタデータを含めずにドキュメント構造を効率的にコピーできます。もしくは、ページを個別にadd_page()で追加し、add_metadata()を呼び出さないことでも同様の結果が得られます。

7. まとめと今後の展望

このブログでは、Pythonの強力なPDF操作ライブラリである「pypdf」(旧PyPDF2)の基本的な使い方から、ページの結合・分割、テキスト抽出、暗号化、メタデータ操作といった応用的な機能までを解説しました。

pypdfを使うことで、これまで手作業で行っていたPDFに関する定型的な作業を自動化し、大幅な時間短縮やヒューマンエラーの削減が期待できます。例えば、以下のような応用が考えられます。

  • フォルダ内の複数の請求書PDFを結合して月次レポートを作成する。
  • 大量のPDFドキュメントから特定のキーワードを含むページを抽出し、テキストデータを分析する。
  • 機密情報を含むPDFファイルを一括でパスワード保護する。
  • Webフォームから入力された情報をもとに、定型フォーマットのPDFを生成する(他のライブラリとの連携が必要な場合あり)。

pypdfは活発に開発が続けられており、今後も機能改善や新しい機能の追加が期待されます。公式ドキュメントやGitHubリポジトリを定期的にチェックして、最新情報を追いかけることをお勧めします。

ぜひ、pypdfを活用して、あなたのPythonプロジェクトや業務効率化に役立ててください!Happy PDF processing! 😄💻

コメント

タイトルとURLをコピーしました