PythonでPDFを分割する3つの手法:pypdf、PyMuPDF、PDFMinerを使った実践例

PythonでPDFを扱う際に「ページごとに分割したい」と感じたことはありませんか?
業務レポートの一部だけを抜き出したい、講義資料を問題別に分割したい、あるいは機能を限定した PDF を作りたい。そんなニーズに応えるのが、PDF 分割ライブラリです。
ここでは、Python で最もポピュラーな 3 つのライブラリ pypdfPyMuPDFPDFMiner を使い、実際に PDF を分割する手順を詳細に解説します。

  • それぞれのライブラリが得意とする場面
  • インストールから実装、そして注意点
  • コード例とその解説

ぜひ「PDF 分割 失敗」や「PDF からページを拾う」といった疑問がある読者の参考になれば幸いです。
(実行前に pip install で必要パッケージをインストールしておくようにしてください)


pypdf (旧 PyPDF2) でシンプルに分割

pypdf は「PyPDF2」の後継プロジェクトで、純粋な Python で PDF 操作を行う代表的ライブラリです。
特に「ページ単位の抽出」「ページの結合」「ページの回転」など、典型的な操作は非常にシンプルに書くことができます。

1. インストール

pip install pypdf

2. 基本的な分割手順

  1. PdfReader で元ファイルを読み込む
  2. PdfWriter で新しい PDF を作成
  3. add_page で必要なページだけ追加
  4. PdfWriter.write() でファイルへ書き出し

例:1 ページおきにペアで分割

from pypdf import PdfReader, PdfWriter

def split_every_two(source_path, output_dir):
    reader = PdfReader(source_path)
    total_pages = len(reader.pages)

    for i in range(0, total_pages, 2):
        writer = PdfWriter()
        # ページ1
        writer.add_page(reader.pages[i])

        # ページ2 が存在すれば追加
        if i + 1 < total_pages:
            writer.add_page(reader.pages[i + 1])

        out_path = f"{output_dir}/part_{i//2 + 1}.pdf"
        with open(out_path, "wb") as f_out:
            writer.write(f_out)
        print(f"Generated: {out_path}")

# 使い方
split_every_two("large_document.pdf", "output_parts")

3. メリットとデメリット

観点 メリット デメリット
コードの可読性 単純な API で書きやすい 大量ページ時にメモリをかなり消費
速度 1 ページ単位なら高速 ライブラリ内部で全ページをオブジェクト化
依存性 追加パッケージ不要 画像やアンカーブックマークの扱いが限定的

pypdf は「純粋に PDF のページ構造をそのまま扱いたい」ケースに最適です。
ただ、元ファイルに多くの画像や特殊なフォントが埋め込まれていると、読み込みに時間がかかる場合があります。


PyMuPDF (fitz) で高速・多機能に分割

PyMuPDF(公式名:fitz)は、MuPDF という高速 C ライブラリを Python バインディングしたものです。
ページ内容をそのまま抽出したり、高品質な PDF 出力が必要な場合に有力です。
画像の埋め込みや、注釈(annotation)の保持なども得意範囲です。

1. インストール

pip install PyMuPDF

2. 分割サンプル

PyMuPDF では「ページをレンダリングして画像化」する手段も選べます。
以下では「ページ単位で分割し、注釈もそのまま保持」する例を示します。

import fitz  # PyMuPDF

def split_by_page(source_path, output_dir):
    doc = fitz.open(source_path)
    for i, page in enumerate(doc, start=1):
        # 1 ページずつ新規 PDF へ
        new_doc = fitz.open()
        new_doc.insert_pdf(doc, from_page=i-1, to_page=i-1)
        out_path = f"{output_dir}/page_{i}.pdf"
        new_doc.save(out_path, deflate=True, garbage=4)
        new_doc.close()
        print(f"Saved: {out_path}")

# 実行
split_by_page("example.pdf", "pages")

画像化したい時(JPEG で保存)

def page_to_image(source_path, output_dir, zoom=2, fmt="png"):
    doc = fitz.open(source_path)
    for i, page in enumerate(doc, start=1):
        pix = page.get_pixmap(matrix=fitz.Matrix(zoom, zoom))
        out_path = f"{output_dir}/page_{i}.{fmt}"
        pix.save(out_path)
        print(f"Image: {out_path}")

3. 利点・注意点

観点 利点 注意点
速度 MuPDF の C 実装で高速、メモリ使用量抑制 画像化時は高解像度でメモリ消費が増える
機能 注釈やフォーム要素、暗号化ファイルも扱える 版画や特定のフォントでの描画差異が出ることがある
互換性 PDF 4 以上を完全サポート 旧版 PDF(1.3)で一部機能が使えないことがある

PyMuPDF は「画像化を伴う分割」や「注釈付きでそのまま分割」など、より高機能が必要な場面におすすめです。


PDFMiner.six でテキスト抽出重視の分割

PDFMiner.six は元々「テキスト抽出」専門のライブラリです。
ページごとの「テキスト」「レイアウト情報」を取得したい場合に優れています。
実際に「PDF から特定要素を抽出して新しい PDF を作る」シナリオに適しています。

1. インストール

pip install pdfminer.six

2. テキストで分割する例

以下では、10 ページごとに PDF を分割しつつ、抽出したテキストを併せて保存します。
このサンプルでは、PDFPage.get_pages() でページ番号を取得し、PDFPageInterpreterLAParams でテキストを取得します。

from pdfminer.high_level import extract_text
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from PDFMiner.layout import LAParams
from pdfminer.converter import TextConverter
import io

def paginate_and_extract(source_path, out_dir, chunk=10):
    # 1. 合計ページ数取得
    with open(source_path, 'rb') as fp:
        page_count = sum(1 for _ in PDFPage.get_pages(fp))

    # 2. 分割
    for start in range(1, page_count + 1, chunk):
        end = min(start + chunk - 1, page_count)
        # PDFMiner でページ単位のレンダリング
        output_pdf = fitz.open()
        input_pdf = fitz.open(source_path)
        output_pdf.insert_pdf(input_pdf, from_page=start-1, to_page=end-1)
        out_path = f"{out_dir}/chunk_{start}_to_{end}.pdf"
        output_pdf.save(out_path)
        output_pdf.close()
        input_pdf.close()
        print(f"Chunk PDF: {out_path}")

        # 3. テキスト抽出
        text = extract_text(source_path, page_numbers=list(range(start-1, end)))
        text_path = f"{out_dir}/chunk_{start}_to_{end}.txt"
        with open(text_path, "w", encoding="utf-8") as f:
            f.write(text)
        print(f"Text: {text_path}")

# 実行
paginate_and_extract("full_report.pdf", "chunks", chunk=10)

ポイント

  • extract_text はページ番号を 0 ベースで指定する必要があります。
  • PDF を分割した際は、fitz を併用することで高速に分割できます。

3. メリット・デメリット

観点 メリット デメリット
テキスト抽出 レイアウト保持(行・列)で正確な抽出 画像化 PDF ではテキストが見つからない場合が多い
使い方 文書解析、機械学習前処理に最適 PDF の元構造(画像、図形)は取得できない
速度 低レベルで最適化されている 大量ページ/大ファイルだと時間が掛かる

PDFMiner は「文書内テキストの抽出」や「検索/索引作成」など、テキスト処理を軸にした分割に用いると発揮します。


実務で選ぶ 3 つの分割手段

要件 推奨ライブラリ
① シンプル(単純ページ抜き取り) pypdf
② 注釈・画像付きで高速かつ高品質 PyMuPDF
③ テキスト抽出が主目的 PDFMiner.six
④ 既存の PDF 標準機能(ページ削除・回転等)を全うしたい pypdf または PyMuPDF

選択フロー例

  1. 「ページ単純分割」pypdf
  2. 「画像・注釈を保持」PyMuPDF
  3. 「本文テキストを大量抽出」PDFMiner.six + PyMuPDF

よくある問題とその対策

問題 原因 対策
「ページが読み込めない」 フォントが埋め込まれていない、または暗号化 pypdfdecrypt(key) や、PyMuPDFopen(filename, filetype="pdf", fileobj=... ) を試す
**「分割後にページが欠落」 PyMuPDFinsert_pdf するときに to_page を 0 ベースにしない from_page=i-1, to_page=i-1 のように 0 ベースに統一
「大容量 PDF でメモリが逼迫」 すべてのページを一度に読み込んでいる fitz.open(...).select で必要ページだけ読み込む、または PDFPage.get_pages() で 1 ページずつ処理
「抽出したテキストに余計な改行が入る」 PDF 版画が横断線で改行として認識される LAParams()all_texts=True を設定し、テキスト結合方法を調整
「ページを JPEG で出力したい」 fitz.Matrix のスケーリングが不十分 zoom_x = zoom_y = 2.0 など解像度を上げる

まとめ

  • pypdf は「シンプルなページ操作」に最適。コードが読みやすく、軽量化が容易。
  • PyMuPDF は「高品質・高速」。注釈保持や画像化も可能で、ビギナーからプロフェッショナルまで幅広く使える。
  • PDFMiner.six は「テキスト抽出重視」。解析やデータ取得のステップとして PDF を読むのに最適。

それぞれのライブラリには特性があり、プロジェクトに合わせて使い分けることで、PDF 分割作業をスムーズに行えます。実際の業務や開発環境に合わせて、ぜひ試してみてくださいね。

コメント