0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】PyMuPDFでPDFから表をズバリ抽出!基本から応用まで徹底解説

Posted at

はじめに

PDFからデータを抽出する際、特に「表(テーブル)」の抽出は多くの開発者が直面する課題です。請求書、報告書、論文など、多くのビジネスドキュメントには構造化されたデータが表形式で含まれており、これを手作業でコピー&ペーストするのは非効率極まりありません。

世の中には多くのPDFライブラリが存在しますが、速度、機能性、そして堅牢性の観点から「PyMuPDF」は非常に強力な選択肢です。

この記事では、PyMuPDFを使ってPDF内の表を抽出するための、基本的かつ実践的な方法をコード付きで詳しく解説します。

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

  • 罫線のある基本的な表を簡単に抽出する
  • 罫線のない表をテキストの配置を元に認識して抽出する
  • 複雑なレイアウトのページから特定の表だけを狙って抽出する
  • 抽出したデータをPandas DataFrameやMarkdown形式で活用する

PyMuPDFとは? なぜ選ぶべきか?

[cite_start]PyMuPDFは、単なるPythonライブラリではなく、高性能なC言語ライブラリ「MuPDF」をPythonから使えるようにしたものです [cite: 4][cite_start]。このアーキテクチャにより、他の多くのライブラリを圧倒する処理速度を実現しています [cite: 4]。

  • [cite_start]圧倒的な速度: 大量のドキュメントを高速に処理する必要がある場合に最適です [cite: 4]。
  • [cite_start]多機能性: テキストや画像の抽出はもちろん、今回解説する高機能な表認識も可能です [cite: 7]。
  • [cite_start]堅牢性: 多少壊れているPDFファイルでも、内部で自動的に修復を試みてくれるため、エラーで処理が止まりにくいという実用上の大きなメリットがあります [cite: 9]。

インストールと準備

まずはPyMuPDFをインストールしましょう。

pip install pymupdf
# データ分析でよく使うpandasも入れておくと便利です
pip install pandas
import fitz  # PyMuPDFをインポート
import pandas as pd

PyMuPDFを使う際の注意点として、ライブラリ名は

pymupdfですが、インポートする際は歴史的な経緯からfitzという名前を使います 。

基本編: 罫線のある表を抽出する
最もシンプルなケースは、下図のように罫線(線画)で明確にセルが区切られている表です。

このような表は、page.find_tables() メソッドで非常に簡単に抽出できます。

page.find_tables() の基本的な使い方

page.find_tables() は、ページ内で見つかった全てのテーブルを認識し、TableFinder というオブジェクトを返します 。このオブジェクトには、見つかったテーブルのリストが含まれています。

import fitz
import pandas as pd

try:
    # PDFファイルを開く
    doc = fitz.open("sample.pdf")
    page = doc[0]  # 最初のページを取得

    # ページ内のテーブルを検索
    tables = page.find_tables()

    if tables.tables:  # テーブルが見つかったか確認
        # 最初のテーブルを取得
        table_data = tables[0].extract()
        
        # 1. Pythonのリストとして出力
        print("--- Pythonリスト ---")
        for row in table_data:
            print(row)
            
        # 2. Pandas DataFrameに変換して出力
        print("\n--- Pandas DataFrame ---")
        df = tables[0].to_pandas()
        print(df)
        
    else:
        print("テーブルが見つかりませんでした。")

    doc.close()

except Exception as e:
    print(f"エラーが発生しました: {e}")

コード解説

  1. fitz.open("sample.pdf") でPDFドキュメントを開きます。
  2. doc[0] のようにインデックスでページオブジェクトを取得します。
  3. page.find_tables() を実行して、ページ内のテーブルを探します 。
  4. tables.tables でテーブルのリストが空でないことを確認します。
  5. tables[0] で最初のテーブルオブジェクトを取得します。
  6. .extract() メソッドで、テーブルの全セル内容をPythonのリストのリストとして取得できます 。
  7. .to_pandas() メソッドを使えば、直接 Pandas DataFrame に変換でき、その後のデータ分析に非常に便利です 。

応用編1: 罫線のない表を抽出する (strategy)

現実のドキュメントでは、下図のように罫線が全くない、スペースで整形されただけの表も頻繁に登場します。

デフォルトの設定では、このような表はうまく認識できません。ここで重要になるのが strategy パラメータです。

page.find_tables() は、テーブルを認識するための複数の「戦略(strategy)」を持っており、これを切り替えることで様々な形式の表に対応できます 。

  • strategy="lines" (デフォルト): ページ内の**線画(ベクターグラフィックス)**を頼りにテーブルの構造を解析します 。罫線があれば高速かつ正確です。
  • strategy="text": 線画がない場合に、単語の垂直・水平方向の配置を分析して列や行の境界を推測します 。計算コストは高くなりますが、罫線なしテーブルに非常に強力です。
# ... (前略) ...
# strategy="text" を指定してテーブルを検索
tables = page.find_tables(strategy="text")

if tables.tables:
    df = tables[0].to_pandas()
    print(df)
else:
    print("テキスト配置ベースでもテーブルは見つかりませんでした。")

# ... (後略) ...

たったこれだけで、罫線がない表も抽出できるようになります。もし表の抽出がうまくいかない場合は、まず strategy を変更してみるのが定石です。

応用編2: ページ内の特定領域から表を抽出する (clip)

請求書のように、1ページ内にヘッダー情報、明細表、フッター情報などが混在している場合、ページ全体を対象にするとアルゴリズムが混乱し、表を誤認識したり、全く検出できなかったりすることがあります。

このような場合は、

clip パラメータが絶大な効果を発揮します。clip に矩形(fitz.Rect)を指定することで、テーブルを探す範囲をその矩形内だけに限定できます 。

import fitz
import pandas as pd

try:
    doc = fitz.open("invoice.pdf")
    page = doc[0]

    # 表が存在するであろう領域をRect(x0, y0, x1, y1)で指定
    # 座標はPDFの左上隅が(0, 0)
    search_area = fitz.Rect(50, 200, 550, 600)

    # 探索範囲を限定してテーブルを検索
    tables = page.find_tables(clip=search_area)

    if tables.tables:
        df = tables[0].to_pandas()
        print(df)
    else:
        print("指定範囲内にテーブルが見つかりませんでした。")

    doc.close()

except Exception as e:
    print(f"エラーが発生しました: {e}")

clip を使うメリット

精度の向上: 関係のないテキストや図形を探索対象から除外することで、表認識のアルゴリズムが正しく機能しやすくなります 。

処理速度の向上: 探索範囲が狭まるため、特に strategy="text" のような計算コストの高い処理を高速化できます。

clipで指定する座標は、page.get_text("words") などで特定のキーワード(例:「品番」「金額」など)の位置を探し、その周辺領域を動的に決定すると、より柔軟で堅牢なシステムを構築できます。

抽出データの活用: LLM時代に最適なMarkdown変換

抽出したデータをCSVやExcelで出力するのも良いですが、PyMuPDFは現代的なワークフローにも対応しています。

table.to_markdown() メソッドを使えば、抽出したテーブルを簡単にMarkdown形式に変換できます 。

# ... (前略) ...
tables = page.find_tables()
if tables.tables:
    # Markdown形式で出力
    markdown_table = tables[0].to_markdown()
    print(markdown_table)

出力例:

| 商品名 | 単価 | 数量 | 金額(円) |
|:---|---:|---:|:---|
| PyMuPDFライセンス | 50,000 | 2 | 100,000 |
| テクニカルサポート | 30,000 | 1 | 30,000 |
| ... | ... | ... | ... |

Markdownは人間にとって読みやすいだけでなく、

大規模言語モデル(LLM)が最も理解しやすい構造化フォーマットの一つです 。抽出した表データを直接LLMへの入力(プロンプト)に含めることで、RAG (Retrieval-Augmented Generation) のようなシステムで、PDFの内容に基づいた要約や質疑応答の精度を大きく向上させることができます 。

まとめ

今回は、PyMuPDFを使ってPDFから表を抽出する方法を、基本から応用まで解説しました。

  • 基本: page.find_tables() で罫線のある表を抽出する。

  • 応用1 (strategy): strategy="text" で罫線のない表を抽出する。

  • 応用2 (clip): clip=fitz.Rect(...) で探索範囲を絞り、精度と速度を向上させる。

  • 活用: .to_pandas() でデータ分析に、.to_markdown() でLLM連携に。

PyMuPDFの表抽出機能は非常に強力ですが、完璧ではありません。重要なのは、

strategyclip といったパラメータを駆使し、「まず試してみて、ダメなら別の戦略でアプローチする」という反復的なワークフローを構築することです 。

この記事が、皆さんのPDFデータ抽出業務の効率化に繋がれば幸いです。

Happy Hacking!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?