PythonによるPDF帳票の自動抽出パイプライン

請求書や支払明細、売上レポートを受け取るたびに、金額や日付をExcelへ転記していないでしょうか。

PDFを開き、必要な箇所を探し、数字をコピーし、入力結果を確認する。この作業は1件なら短くても、毎月繰り返せば時間を奪います。入力ミスが請求漏れや集計誤差につながる危険もあります。

PythonでPDFから情報抽出する仕組みを作れば、帳票の受信からデータ保存、異常検知までを自動化できます。ただし、pdfplumberで文字を読み取るコードを書いたところで、すぐ無人運転できるわけではありません。PDFには文字を直接取得できるものと、OCRが必要な画像型があり、レイアウト変更や重複処理にも備える必要があるからです。

この記事では、初心者でも実装に着手できるように、次の成果を目指します。

  • PDFの種類に合う抽出方法を選べる
  • 誤抽出を後工程へ流さない検証ルールを作れる
  • 失敗した帳票だけを安全に隔離できる
  • 人間が毎回操作しなくても動く処理系を設計できる
  • 抽出データを請求管理や収益監視へ接続できる

単発の時短ツールではなく、繰り返し働く自動化資産として設計する方法を扱います。

PythonによるPDF情報抽出の全体像

PDF情報抽出は、次の流れに分けると理解しやすくなります。

PDF受信
重複判定
テキスト型・画像型の判定
テキスト抽出またはOCR
項目抽出
形式・金額・整合性の検証
  ├─ 正常 → CSV・DB・会計システムへ保存
  └─ 異常 → 隔離フォルダへ移動して通知

OCRとは、画像内の文字を読み取る技術です。たとえば、紙の請求書をスキャンしたPDFでは、画面上に「請求金額 128,000円」と見えていても、内部に文字データがありません。この場合はOCRを利用します。

工程を分離する理由は、障害の影響範囲を狭くするためです。取引先が帳票デザインを変更しても、受信処理や保存処理まで作り直す必要はありません。抽出ルールだけを差し替えられます。

PDFは大きく3種類に分ける

種類具体例主な手段注意点
テキストPDF会計ソフトから出力した請求書pdfplumber、PyMuPDF読み順が見た目と異なることがある
画像PDF紙をスキャンした領収書OCR、pytesseract傾きや低解像度で誤読しやすい
混在PDF表紙は画像、明細はテキストページ別判定ファイル単位の一律判定では取りこぼす

PDF全体を一種類として扱わず、ページごとに抽出可能な文字数を調べる設計にすると混在PDFにも対応できます。

このサイトの一次データから見えた設計上の教訓

2026年7月22日に、このサイトの運用リポジトリをPowerShellで調査しました。調査対象は sites/ai-tech/content/posts にあるMarkdownファイルです。

(Get-ChildItem sites\ai-tech\content\posts -File -Filter *.md).Count

結果は363ファイルでした。さらにタイトルを確認すると、PythonによるPDF帳票抽出を直接扱う記事は3本ありました。これは一般市場の記事総数ではなく、当サイトの該当ディレクトリを同日に調べた結果です。

生成状態を保存する generator/.state.json には90件の生成履歴がありました。品質基準を管理する generator/ai_slop_guidelines.json には10項目の検査条件と、合格目安として8点が設定されています。同ファイルに記録された基準取得日時は2026年6月26日です。

この運用記録から得られる教訓は、データを作る処理と品質を確認する処理を分けることです。PDF抽出でも、「値らしい文字が取れた」と「業務に使える正しい値が取れた」は別の状態です。

既存の類似記事はOCR判定やライブラリ選定を中心にしていました。本稿では一歩進めて、次の4点を設計の中心に置きます。

  • 項目単位の根拠保存
  • 誤抽出時の停止条件
  • 冪等性による二重処理防止
  • 人間確認を減らす改善ループ

冪等性とは、同じ処理を複数回実行しても結果が重複しない性質です。たとえば同じ請求書PDFを2回受信しても、売上データを1件だけ登録する設計を指します。

ステップ・バイ・ステップ:PDF情報抽出を作る9工程

1. 実物のPDFを集めて分類する

最初に対象帳票を集めます。学習用のきれいなサンプルだけでは、実務で発生する例外を発見できません。

初期調査では、次の違いが分かるPDFを用意します。

  • 発行元が異なる
  • 1ページと複数ページがある
  • テキスト型とスキャン型がある
  • 金額表記に「円」「¥」「税込」が混在する
  • パスワード付きや破損ファイルが含まれる
  • 表の途中で改ページされる
  • 訂正版や再発行版がある

仮に20件を検証用に集めるなら、「同じ発行元の請求書20件」なのか「10社から2件ずつ」なのかを記録してください。前提が違えば難易度も変わります。20件という数は業界標準ではなく、初期調査用の例です。

2. 必要項目と許容誤差を定義する

コードを書く前に、抽出仕様を表にします。

項目値の例必須検証条件失敗時
請求書番号INV-2026-0712必須空欄不可処理停止
請求日2026-07-12必須日付として解釈可能処理停止
請求金額128,000円必須数値化可能、0円以上隔離
登録番号T1234567890123任意Tと13桁警告
備考7月利用分任意条件なし空欄許可

金額や支払期限は、誤りが損失につながる項目です。備考と同じ扱いにしてはいけません。必須項目、検証方法、失敗時の動作をセットで決めます。

3. テキスト型か画像型かを自動判定する

まず通常のテキスト抽出を試し、取得文字数や必須ラベルの有無からOCRへ切り替えます。

import fitz  # PyMuPDF

def extract_page_text(page) -> tuple[str, str]:
    text = page.get_text("text").strip()

    if len(text) >= 30:
        return text, "embedded_text"

    return run_ocr(page), "ocr"

30は説明用の仮値です。実際には、対象PDFからページ別文字数を計測し、空白ページや表紙が誤ってOCRへ送られない境界値を決めます。

文字数だけでなく、請求金額Invoice No.など、想定ラベルが存在するかも確認すると判定が安定します。

4. 帳票テンプレートを識別する

取引先ごとにレイアウトが違う場合、すべてを一つの正規表現で処理するとルールが複雑になります。

そこで、会社名、帳票タイトル、固定ラベル、ページサイズなどからテンプレートIDを決めます。

def detect_template(text: str) -> str:
    if "株式会社青空商事" in text and "御請求書" in text:
        return "aozora_invoice_v1"
    if "ACME SERVICE" in text and "INVOICE NO." in text:
        return "acme_invoice_v2"
    return "unknown"

unknownを無理に処理せず、未対応帳票として隔離します。誤ったルールを適用して正常扱いするより、調査対象として残すほうが安全です。

5. ラベル・座標・正規表現を使い分ける

抽出方法には向き不向きがあります。

  • 正規表現請求番号: INV-1234のように形式が決まっている値
  • ラベル近傍探索請求金額の右側や次の行に値がある帳票
  • 座標抽出:毎回同じ位置に金額が印字される固定帳票
  • 表抽出:品目、数量、単価が行列で並ぶ明細
  • OCR:画像として保存された文字
  • AI補助:レイアウトが多様で、ルール化が難しい補足欄

金額の単純な例は次のようになります。

import re
from decimal import Decimal

def extract_amount(text: str) -> Decimal | None:
    match = re.search(r"請求金額[::\s]*[¥¥]?\s*([\d,]+)\s*円?", text)
    if not match:
        return None

    return Decimal(match.group(1).replace(",", ""))

AIやLLMへ帳票全体を渡す方法は柔軟ですが、出力が毎回同じとは限らず、機密情報の取り扱いも必要です。日付、金額、番号など形式が明確な項目は、決定的に動くルールを優先します。

6. 値と一緒に抽出根拠を保存する

抽出結果には値だけでなく、出典となるページ番号、抽出方式、元文字列を持たせます。

{
  "field": "invoice_total",
  "value": 128000,
  "page": 1,
  "method": "label_regex",
  "source_text": "ご請求金額 ¥128,000",
  "template_id": "aozora_invoice_v1"
}

後から金額が違うと判明したとき、PDFを最初から読み直さなくても原因を追えます。この記録は監査ログ、つまり「いつ、どの根拠から値を登録したか」の履歴にもなります。

7. 業務ルールで検算する

形式が正しくても、値が正しいとは限りません。次の検算を組み合わせます。

  • 明細の合計と請求合計が一致する
  • 消費税額が前提の税率と矛盾しない
  • 支払期限が請求日より前になっていない
  • 請求書番号が過去データと重複していない
  • 取引先名と振込先口座の組み合わせが登録情報と合う
  • 通貨単位が前月と突然変わっていない

消費税の計算には端数処理の違いがあるため、「一致しない=必ず誤り」とは限りません。帳票の切り捨て・切り上げ規則を確認し、許容差を仕様に残します。

8. ハッシュで重複を防ぎ、処理状態を管理する

ファイル内容からSHA-256ハッシュを作り、処理済みか判定します。

from hashlib import sha256
from pathlib import Path

def file_hash(path: Path) -> str:
    return sha256(path.read_bytes()).hexdigest()

同じハッシュが登録済みなら再処理しません。ただし、ファイル名が同じでも内容が更新される場合があります。そのため、ファイル名ではなく内容のハッシュを使います。

状態は少なくとも次のように分けます。

received → classified → extracted → validated → exported
                                  └→ quarantined

途中で停止したPDFだけを再開できれば、正常な帳票まで処理し直さずに済みます。

9. 定期実行・通知・次工程まで接続する

最後に、Windowsタスクスケジューラ、cron、GitHub Actionsなどで定期実行します。メールや共有フォルダからPDFを取得し、正常データをCSVやデータベースへ保存します。

収益につなげるなら、抽出後の判断まで自動化します。

  • 売上レポートから利益率低下を検知する
  • 請求書と入金明細を照合して未入金候補を通知する
  • 広告明細から費用対効果の悪化を検知する
  • ポイント明細から失効予定を抽出して通知する
  • 発注書から在庫不足候補を登録する

これらは収益を保証する仕組みではありません。規約違反となるポイント獲得操作や、サービスが禁止する自動アクセスは避けてください。ここで扱うのは、自分が適法に取得した帳票を整理し、判断に必要な情報を届ける自動化です。

PDF抽出結果と例外帳票の監視画面

専門家目線のチェックポイント

座標より「意味」を優先できるか

座標抽出は高速ですが、余白が数ミリ変わっただけで壊れる可能性があります。同じ発行元の固定帳票には有効でも、複数社の請求書にはラベル近傍探索が向いています。

判断基準は次の通りです。

  • 年単位で様式が固定される帳票:座標を候補にする
  • 項目の位置だけ変わる帳票:ラベルを基準にする
  • 列順が変わる明細:見出し名から列を特定する
  • 発行元が多い帳票:テンプレート分類を先に行う
  • 手書き文字を含む帳票:無人確定の対象外も検討する

「読み取れない」と「誤って読み取る」を分ける

空欄なら処理を停止できます。一方、83として抽出すると、正常な数値に見えて後工程へ流れる危険があります。

特に金額、口座番号、契約番号では、OCRの信頼度だけに頼らず、明細合計やマスターデータとの照合を加えます。

人間確認をゼロにできないケースを認める

次のPDFは完全無人化に適さない場合があります。

  • 手書きが多い
  • 写真がぼやけている
  • 表の上に印鑑が重なっている
  • 毎回レイアウトが変わる
  • 法的判断や承認を伴う
  • 誤登録時の損失が大きい
  • 外部サービスへの送信が規約や契約で制限される

この場合は、低信頼度データだけを確認画面へ回します。人間を全工程から外すのではなく、確認が必要な例外を減らし続ける設計が現実的です。

画像・スクリーンショットで説明すべき箇所

記事や社内手順書には、次の視覚資料を入れると理解が深まります。

  1. テキストPDFと画像PDFの比較画面
    左側に文字を選択できるPDF、右側にスキャン画像を配置し、抽出文字数も表示します。

  2. 抽出根拠のレビュー画面
    PDF上の「請求金額」を色枠で囲み、右側に抽出値、ページ番号、方式、検証結果を並べます。

  3. 運用ログのスクリーンショット
    成功、隔離、重複スキップ、未対応テンプレートを色分けします。視覚的証拠として、件数だけでなく対象期間と母数も写します。

  4. 処理パイプライン図
    受信から保存までの分岐と、失敗時にどこへ移動するかを示します。

機密情報があるPDFを掲載するときは、氏名、住所、口座番号、請求書番号をマスキングしてください。画像生成サービスへ実帳票を送信する場合も、利用規約とデータ保持条件の確認が必要です。

よくある失敗と対策

文字が一切取れない

原因: スキャンPDFをテキスト型として処理している。

対策: ページごとの抽出文字数を測り、OCRへ分岐します。OCR前に傾き補正とノイズ除去を行うと改善する場合があります。

「128,000円」を数値化できない

原因: カンマ、通貨記号、全角数字、空白が混在している。

対策: 抽出と正規化を分けます。128,000円128000へ変換してから数値型にします。小数を扱う金額には浮動小数点ではなくDecimalを使います。

明細の列がずれる

原因: PDFにはExcelのようなセル構造がなく、文字が座標として配置されている。

対策: 見出し位置、文字のX座標、罫線を組み合わせます。複数ページの表では、各ページに繰り返されるヘッダー行を除外します。

帳票改訂のたびに停止する

原因: すべての取引先を一つのルールで処理している。

対策: テンプレートIDとバージョンを管理します。未知のレイアウトを自動確定せず、隔離して新しいルールの候補にします。

自動化したのに確認作業が減らない

原因: すべての抽出結果を人間が再確認している。

対策: 必須項目、検算結果、信頼度からレビュー条件を決めます。正常条件を満たす帳票は自動保存し、例外だけを確認対象にします。

失敗理由が分からず毎回PDFを開く

原因: 成否だけをログに残している。

対策: ファイルハッシュ、テンプレートID、失敗項目、ページ番号、元文字列、例外名を記録します。機密データをログへ出しすぎないよう、口座番号などは一部を伏せます。

成果を測るKPI

KPI計算方法改善の読み方
自動完了率正常保存件数 ÷ 受信件数無人で完結した割合
項目正解率正しかった項目数 ÷ 確認項目数抽出品質
隔離率隔離件数 ÷ 受信件数例外処理の負担
未対応テンプレート率未対応件数 ÷ 受信件数ルール追加の優先度
重複防止件数ハッシュで除外した件数二重登録の抑止効果
1件あたり人間作業時間確認時間 ÷ 処理件数時間消耗の残り具合
回収期間初期構築時間 ÷ 月間削減時間開発工数を時間で回収する目安
下流エラー率出力後の訂正件数 ÷ 保存件数業務影響を含む品質

たとえば「自動完了率90%」と記録する場合は、対象期間、母数、帳票の種類、手書きの有無も添えます。「A社のテキスト型請求書100件、2026年7月、手書きなし」のように条件を残せば、翌月と比較できます。

収益額をKPIにすると外部要因の影響が大きくなります。まずは削減時間、未入金候補の検知件数、転記訂正件数など、仕組みが直接動かせる指標を追います。投資判断や将来収益を保証するものではなく、業務改善を評価するための一般的な指標です。

実装前チェックリスト

  • 実物PDFを発行元・形式・ページ数で分類した
  • 必須項目と任意項目を分けた
  • 項目ごとの停止条件を決めた
  • テキスト型と画像型の分岐がある
  • 未知の帳票を正常扱いしない
  • 値と一緒に抽出根拠を保存する
  • 明細合計などの業務検算がある
  • ファイルハッシュで重複を防ぐ
  • 例外PDFを隔離できる
  • 途中から安全に再実行できる
  • ログに機密情報を残しすぎていない
  • 利用規約やデータ送信条件を確認した

今日すぐできる具体的アクション

まず、同じ種類のPDFを手元から集め、次の4列を持つ表を作ってください。

項目名 | 実際の値 | 必須か | 間違いを検知する方法

請求金額なら「128,000円|必須|明細合計と一致」、請求日なら「2026-07-12|必須|日付変換できる」と記載します。

その後、Pythonで1件だけテキストを抽出し、文字が取れるか確認します。ここまで進めれば、OCRが必要か、正規表現で対応できるか、テンプレート分類が必要かを判断できます。

まとめ:PDFを読むコードから、止まらず改善できる自動化資産へ

PythonによるPDF情報抽出では、抽出ライブラリの選択に加えて、分類、検算、重複防止、隔離、ログ、再実行まで設計する必要があります。

目指す処理の流れは明確です。

  1. PDFを自動受信する
  2. 種類とテンプレートを判定する
  3. 必要項目と抽出根拠を保存する
  4. 業務ルールで検算する
  5. 正常データだけを次工程へ渡す
  6. 例外だけを隔離して通知する
  7. ログから失敗の多い帳票を改善する

この循環が動けば、人間はすべてのPDFを開く作業から離れられます。請求漏れ、未入金、費用増加、ポイント失効などを検知する処理へ接続すれば、単なる転記ツールではなく、時間と機会損失を守る自動化資産になります。

完全放置や収益は保証できません。帳票改訂、OCR誤読、外部サービス障害への保守は残ります。それでも、失敗を隔離して再実行できる仕組みなら、手作業へ全面的に逆戻りする可能性を抑えられます。

本気で自動化・不労所得を構築したい方向けの実践マニュアル

PDF抽出を作っても、その後に人間がCSVを開き、数字を確認し、メールを送り、販売状況を集計していたら、時間は別の工程で消えていきます。

手離れのよい仕組みを作るには、データ取得、判定、通知、販売、記録、改善を一本の流れとして設計する必要があります。最初から巨大なシステムを作る必要はありません。毎月発生する一つの作業を選び、人間の操作が残る場所を順番に減らしていく方法があります。

「自分が作業し続けなければ止まる副業」から、「自分が離れている間も価値を生み続ける自動化資産」へ移行したい方へ、実装手順と収益導線をまとめた実践マニュアルを用意しています。

次に仕組み化する作業を、今日決めてください。

本気で自動化・不労所得を構築する実践マニュアルを見る