PDFファイルを結合・分割するPythonスクリプト

プログラミング

PDFファイルを結合・分割するPythonスクリプト

Pythonを使用してPDFファイルを結合したり分割したりすることは、多くの場面で役立ちます。例えば、複数の論文や契約書を一つのPDFにまとめたい場合、あるいは大きなPDFファイルを扱いやすいサイズに分割したい場合などが考えられます。ここでは、PyPDF2というライブラリを用いて、これらの操作を行うPythonスクリプトについて解説します。

PyPDF2ライブラリの概要

PyPDF2は、PDFファイルの操作を簡単に行うためのPythonライブラリです。PDFファイルの読み込み、書き込み、結合、分割、ページ操作(回転、トリミングなど)、メタデータの取得・設定など、多様な機能を提供しています。PDFファイルを扱う際には、このライブラリをインストールしておくことが第一歩となります。

PyPDF2のインストール

PyPDF2pipコマンドを使用して簡単にインストールできます。ターミナルまたはコマンドプロンプトを開き、以下のコマンドを実行してください。

pip install pypdf2

インストールが完了すると、Pythonスクリプト内でimport PyPDF2としてライブラリを利用できるようになります。

PDFファイルの結合スクリプト

複数のPDFファイルを一つのPDFファイルに結合するスクリプトを作成します。このスクリプトでは、指定された複数のPDFファイルを順番に読み込み、それらを新しいPDFファイルに書き出します。

結合スクリプトのコード例

import PyPDF2
import os

def merge_pdfs(input_pdfs, output_pdf):
    """
    複数のPDFファイルを一つに結合する関数。

    Args:
        input_pdfs (list): 結合したいPDFファイルのパスのリスト。
        output_pdf (str): 結合後のPDFファイルの出力パス。
    """
    pdf_merger = PyPDF2.PdfMerger()

    for pdf in input_pdfs:
        try:
            with open(pdf, 'rb') as file:
                pdf_merger.append(file)
        except FileNotFoundError:
            print(f"エラー: ファイル '{pdf}' が見つかりません。スキップします。")
        except Exception as e:
            print(f"エラー: ファイル '{pdf}' の処理中に問題が発生しました - {e}")

    try:
        with open(output_pdf, 'wb') as file:
            pdf_merger.write(file)
        print(f"PDFファイル '{output_pdf}' が正常に結合されました。")
    except Exception as e:
        print(f"エラー: 結合後のPDFファイル '{output_pdf}' の書き込み中に問題が発生しました - {e}")

if __name__ == "__main__":
    # 結合したいPDFファイルのリストを指定します
    # 例: input_files = ["doc1.pdf", "doc2.pdf", "report.pdf"]
    input_files = ["file1.pdf", "file2.pdf"] # ここに結合したいPDFファイルのパスを記述します

    # 結合後の出力ファイル名を指定します
    output_file = "merged_document.pdf"

    # 結合処理を実行します
    merge_pdfs(input_files, output_file)

コードの説明

  • PyPDF2.PdfMerger(): PDFマージャーオブジェクトを作成します。このオブジェクトが、PDFファイルの追加や書き出しの操作を行います。
  • pdf_merger.append(file): 指定されたPDFファイルをマージャーに追加します。'rb'モードでファイルを開くことで、バイナリ読み込みを行います。
  • pdf_merger.write(file): マージャーに追加されたすべてのPDFページを、指定された出力ファイルに書き出します。'wb'モードでファイルを開き、バイナリ書き込みを行います。
  • try-exceptブロック: ファイルが見つからない場合や、その他のエラーが発生した場合に、プログラムがクラッシュするのを防ぎ、ユーザーにエラーメッセージを表示します。
  • if __name__ == "__main__":: このブロックは、スクリプトが直接実行された場合にのみ実行されます。ここで、結合したいPDFファイルのリストと出力ファイル名を指定します。

PDFファイルの分割スクリプト

一つのPDFファイルを、指定したページ範囲または1ページずつに分割するスクリプトを作成します。

分割スクリプトのコード例

import PyPDF2
import os

def split_pdf_by_pages(input_pdf, output_prefix="page"):
    """
    PDFファイルを1ページずつ分割する関数。

    Args:
        input_pdf (str): 分割したいPDFファイルのパス。
        output_prefix (str): 分割後のファイル名のプレフィックス。
    """
    try:
        with open(input_pdf, 'rb') as file:
            pdf_reader = PyPDF2.PdfReader(file)
            num_pages = len(pdf_reader.pages)

            if num_pages == 0:
                print(f"エラー: PDFファイル '{input_pdf}' にページがありません。")
                return

            for i in range(num_pages):
                pdf_writer = PyPDF2.PdfWriter()
                pdf_writer.add_page(pdf_reader.pages[i])

                output_filename = f"{output_prefix}_{i+1}.pdf"
                try:
                    with open(output_filename, 'wb') as output_file:
                        pdf_writer.write(output_file)
                    print(f"ページ {i+1} を '{output_filename}' として保存しました。")
                except Exception as e:
                    print(f"エラー: ファイル '{output_filename}' の書き込み中に問題が発生しました - {e}")

    except FileNotFoundError:
        print(f"エラー: ファイル '{input_pdf}' が見つかりません。")
    except Exception as e:
        print(f"エラー: PDFファイル '{input_pdf}' の処理中に問題が発生しました - {e}")

def split_pdf_by_range(input_pdf, page_ranges, output_prefix="part"):
    """
    PDFファイルを指定したページ範囲で分割する関数。

    Args:
        input_pdf (str): 分割したいPDFファイルのパス。
        page_ranges (list): 分割したいページ範囲のリスト。例: [[1, 5], [6, 10]]
        output_prefix (str): 分割後のファイル名のプレフィックス。
    """
    try:
        with open(input_pdf, 'rb') as file:
            pdf_reader = PyPDF2.PdfReader(file)
            num_pages = len(pdf_reader.pages)

            if num_pages == 0:
                print(f"エラー: PDFファイル '{input_pdf}' にページがありません。")
                return

            for i, page_range in enumerate(page_ranges):
                start_page, end_page = page_range
                pdf_writer = PyPDF2.PdfWriter()

                if not (1 <= start_page <= end_page <= num_pages):
                    print(f"警告: ページ範囲 [{start_page}, {end_page}] は無効です。スキップします。")
                    continue

                for page_num in range(start_page - 1, end_page):
                    pdf_writer.add_page(pdf_reader.pages[page_num])

                output_filename = f"{output_prefix}_part{i+1}.pdf"
                try:
                    with open(output_filename, 'wb') as output_file:
                        pdf_writer.write(output_file)
                    print(f"ページ範囲 {page_range} を '{output_filename}' として保存しました。")
                except Exception as e:
                    print(f"エラー: ファイル '{output_filename}' の書き込み中に問題が発生しました - {e}")

    except FileNotFoundError:
        print(f"エラー: ファイル '{input_pdf}' が見つかりません。")
    except Exception as e:
        print(f"エラー: PDFファイル '{input_pdf}' の処理中に問題が発生しました - {e}")


if __name__ == "__main__":
    # --- 1ページずつ分割する場合 ---
    # input_document = "large_report.pdf" # 分割したいPDFファイルのパス
    # split_pdf_by_pages(input_document, output_prefix="report_page")

    # --- 指定したページ範囲で分割する場合 ---
    input_document_range = "manual.pdf" # 分割したいPDFファイルのパス
    # 例: 1ページ目から5ページ目、6ページ目から10ページ目、というように指定
    # ページ番号は1から始まります
    ranges_to_split = [[1, 5], [6, 10], [11, 15]] # ここに分割したいページ範囲を記述します
    split_pdf_by_range(input_document_range, ranges_to_split, output_prefix="manual_section")

コードの説明

  • PyPDF2.PdfReader(file): PDFファイルを読み込むためのリーダーオブジェクトを作成します。
  • len(pdf_reader.pages): PDFファイルに含まれる総ページ数を取得します。
  • pdf_reader.pages[i]: 指定したインデックスiのページを取得します。ページインデックスは0から始まります。
  • PyPDF2.PdfWriter(): 新しいPDFファイルを作成するためのライターオブジェクトを作成します。
  • pdf_writer.add_page(page): リーダーから取得したページをライターに追加します。
  • pdf_writer.write(output_file): ライターに格納されたページを、指定された出力ファイルに書き出します。
  • split_pdf_by_pages関数: PDFファイルを1ページずつ分割します。ループ処理により、各ページを個別のPDFファイルとして保存します。
  • split_pdf_by_range関数: 指定されたページ範囲に基づいてPDFファイルを分割します。page_rangesリストで、開始ページと終了ページをペアで指定します。ページ番号は1から始まるため、リストへのアクセス時には-1する必要があります。

その他のPDF操作

PyPDF2ライブラリは、結合・分割以外にも様々なPDF操作が可能です。

ページ数の取得

PDFファイルが何ページあるかを知りたい場合は、len(reader.pages)で取得できます。

特定のページの抽出

指定したページだけを抽出して新しいPDFファイルを作成することも可能です。これは、split_pdf_by_range関数のように、1ページのみを範囲として指定することで実現できます。

ページの回転

PDFのページを90度、180度、270度回転させることができます。page.rotate(90)のようなメソッドを使用します。

ページのトリミング

ページの余白を削除するなど、表示領域を調整することも可能です。page.cropBoxなどの属性を操作します。

パスワード保護

PDFファイルにパスワードを設定したり、パスワードで保護されたPDFを開いたりすることもできます。

注意事項とベストプラクティス

  • エラーハンドリング: ファイルが存在しない、ファイルが破損している、権限がないなど、様々なエラーが発生する可能性があります。try-exceptブロックを適切に使用して、予期せぬエラーでスクリプトが停止しないようにしましょう。
  • ファイルパス: スクリプトを実行する際は、PDFファイルのパスが正しく指定されていることを確認してください。相対パスまたは絶対パスを使用できます。
  • 文字コード: ファイル名に日本語などの非ASCII文字が含まれる場合、OSの環境によっては文字化けが発生することがあります。utf-8エンコーディングを意識すると良いでしょう。
  • 大きなファイル: 非常に大きなPDFファイルを処理する場合、メモリ使用量が増加する可能性があります。必要に応じて、ファイルを開くモードや処理方法を工夫する必要があります。
  • PDFのバージョン: PyPDF2は、ほとんどのPDFバージョンに対応していますが、非常に特殊な形式のPDFファイルでは問題が発生する可能性もゼロではありません。

まとめ

PyPDF2ライブラリを使用することで、PythonスクリプトからPDFファイルの結合や分割を容易に実行できます。これらのスクリプトは、文書管理、レポート作成、データ処理など、多岐にわたるタスクの自動化に役立ちます。基本的な結合・分割機能に加え、ページ操作などの高度な機能も利用できるため、PDFファイルを効率的に扱うための強力なツールとなります。