ふくふくHukuhuku Inc.
EP.32Data Fetch 10分公開: 2026-09-02

PDF から表を取る ── 最後の手段だと理解したうえで

PDF は印刷のための形式で、表という構造を持っていません。文字が座標付きで並んでいるだけです。だから抽出は必ず推測になります。

#PDF#Python#前処理
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

欲しいデータが にしか無い、という状況があります。行政の資料や企業の開示資料では今でも珍しくありません。画面には整った表が見えているので、取り出せそうに感じます。

が用意されていれば迷わずそちらを使いますが、無いことがあります。ところが PDF には、表という構造がありません。あるのは「この位置にこの文字がある」という情報だけです。人間は罫線と位置関係から表だと認識しますが、機械にとっては座標付きの文字の集まりに過ぎません。

つまり PDF からの表抽出は、必ず推測を含みます。うまくいくこともあれば、列がずれることも、セルが結合されることもあります。この前提を理解したうえで使う必要があります。

文字が入っているかを最初に確かめる

最初にやるべきは、その PDF に文字が入っているかどうかの確認です。紙をスキャンしただけの PDF は、中身が画像なので文字を取り出せません。この場合は が必要になり、難易度が一段上がります。

確認は簡単で、1ページぶんの文字を取り出してみるだけです。何も返ってこなければ画像です。この確認を飛ばして抽出処理を書くと、原因不明の空の結果に悩むことになります

文字が入っているかを先に判定する
Python
import pdfplumber

def has_text_layer(path: str, check_pages: int = 3) -> bool:    """先頭の数ページに文字が入っているかを見る。    入っていなければスキャン画像なので、OCR が要る。"""    with pdfplumber.open(path) as pdf:        for page in pdf.pages[:check_pages]:            text = (page.extract_text() or "").strip()            if text:                return True    return False

def diagnose(path: str) -> str:    if not has_text_layer(path):        return "文字が入っていない。スキャン画像なので OCR が必要"    return "文字が入っている。抽出を試せる"

罫線があるかどうかで手法が変わる

文字が入っている場合、次の分かれ目は罫線の有無です。罫線で区切られた表なら、線の位置を手がかりに列を判定できます。罫線が無く、空白で桁を揃えているだけの表は、文字の座標から列の切れ目を推測することになります。

後者は精度が落ちます。数字の桁数がまちまちだと、同じ列の値が違う位置に来るためです。列がずれていないかを、抽出後に必ず目で確かめてください

PDF の種類と、取り出しやすさ
PDF の状態手法精度
文字あり・罫線あり罫線を手がかりに抽出比較的高い
文字あり・罫線なし座標から列を推測低い。要確認
スキャン画像OCR を通してから抽出内容次第。数字は誤認しやすい
文字あり・多段組段の分割から必要低い
OCR は数字を取り違える

スキャン画像から数字を読み取る場合、1 と 7、0 と 8、6 と 5 の取り違えが起きます。金額や件数を扱うなら、合計との突き合わせなど、別の方法で検算できる仕組みが無い限り実用は難しいと考えてください。

表が複数ページにまたがる場合も要注意です。ページごとに見出しが繰り返されることがあり、それをデータの行として読み込んでしまいます。逆に、2ページ目以降に見出しが無く、列の対応が分からなくなることもあります。

ページをまたいで1つの行が分割されていることもあります。人が見れば続きだと分かりますが、機械には別の行に見えます。ページ単位で抽出してから、自分で繋ぎ直すほうが制御しやすい。

抽出した結果はの形に直してから保存してください。PDF から出てきた直後の形は、元の表の見た目を引きずっているので、そのままでは集計に使えません。

抽出した結果を必ず検算する

PDF からの抽出は推測を含むので、結果が正しいかを別の手段で確かめる必要があります。最も手軽なのが、表に載っている合計行との突き合わせです。明細を足して合計と一致すれば、少なくとも数字の取り違えは起きていません。

合計行と明細の合計を突き合わせる
Python
def verify_total(rows: list[dict], total_row: dict, col: str, tol: float = 0.5) -> None:    """合計行があるなら、必ず突き合わせる。    ずれていれば、抽出の段階で値を取り違えている。"""    detail_sum = sum(r[col] for r in rows if r.get(col) is not None)    stated = total_row.get(col)
    if stated is None:        raise ValueError(f"合計行に {col} が無い")    if abs(detail_sum - stated) > tol:        raise ValueError(            f"{col} の合計が一致しない: 明細 {detail_sum} / 合計行 {stated}"        )

この検算は、EP.13 で扱った取得直後の検算と同じ考え方です。合計行が無い場合は、元の PDF を目で見て数か所を照合するしかありません。全部は無理でも、先頭・末尾・途中の数行を確かめるだけで、列のずれのような大きな失敗には気づけます。

そして最後に。同じデータが他の形式で配布されていないかを、もう一度探してください。PDF の隣に CSVが置かれていることは、実際によくあります。抽出処理を書く時間より、探す時間のほうがずっと短くて済みます。

PDF からの抽出は、うまくいったときでも「たまたま合っていただけ」の可能性が残ります。検算の手段を先に用意してから取りかかってください。

そして繰り返しになりますが、これは最後の手段です。同じ内容が別の形式で出ていないかを、時間をかけてでも先に探す価値があります。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp