PythonでPDFの請求書からデータを抽出する

PythonでPDFから請求書データを抽出するには、ファイルからテキストを読み取り、そのテキストから指定されたフィールドを抜き出します。2つのステップで、おそらく40行のコードがあれば、最初に試す請求書では見事に機能するでしょう。しかし、7番目のサプライヤーが請求書番号を3行上に移動させると、午前2時にパーサーが突然Noneを返し始めます。

この後半部分こそが、この記事の本当のテーマです。読み取り自体は簡単です。正確性を維持し続けることこそが、本当の仕事なのです。

主なポイント

  • Pythonは数行のコードで請求書のテキストを抽出できます。実際には、何百ものサプライヤーのレイアウトに対応し、正確性を維持し続けることにコストがかかります。
  • PDFはデータフォーマットではありません。印刷されたページのタイポグラフィ的な記述に過ぎないため、特定のサプライヤーのレイアウトに対して記述された正規表現は非常に脆いものとなります。
  • 正規表現とサプライヤーごとのテンプレートの管理コストは、サプライヤーの数に比例して増加します。ビジョンモデルは文字列ではなくページ全体を読み取るため、そのように増加することはありません。
  • どの方法でデータを抽出するにせよ、自分自身のコードで算術チェックを行う必要があります。小計と一致しない明細行の合計は、これまでで最も安上がりなバグ検出器となります。
  • 人々が自社開発を諦める理由は、データ抽出そのものにあることは稀です。例外処理キュー、ベンダーのマッチング、そして会計システムとの統合が、本当の障壁なのです。

PDFフォーマットとは

PDFフォーマットは、請求書のような紙の書類を正確にデジタルで表現できる柔軟なフォーマットです。 元々、紙印刷の世界から生まれ、印刷ページをそのままデジタルに反映するように設計されています。 この柔軟性により、PDF作成者は自由に表現でき、さまざまな基準や規制にも対応できます。

一方、PDFの中にデータが閉じ込められていると、後から情報を抽出するのは手間がかかります。 フォーマットが自由で複雑になりやすく、日々大量に処理される業務用途では、構造化データとして管理しづらい場合があります。

A screen capture of PDF file format layers
PDF file format layers

PDFは、各文字がページのどこに配置されているかを保存します。右下の数字が合計金額であるという事実は保存しません。その関係性はあなたの頭の中にあり、この記事で紹介するすべての抽出方法は、それをエンコードする試みです。

請求書からデータを抽出する手順

請求書は一般的にPDFフォーマットでやりとりされます。 請求書は、供給者と顧客の間で、商品やサービスの対価を正式に記録するための書類です。 この請求書から必要なデータを抽出する場合、下記の手順を踏みます:

  1. 抽出したいデータ項目(スキーマ)を決める
  2. 請求書ファイルを画像からテキストに変換する
  3. テキストからスキーマに沿ってデータを取得する
  4. 抽出したデータをまとめる

A screen capture of Invoice data extraction process
Invoice data extraction process

請求書データのスキーマを定義する

請求書はサプライヤーごとに異なるレイアウトで作られるものの、根本的に必要な情報はほぼ同じです。一般的には「供給者」「顧客」「請求書番号」「日付」「商品リスト(数量・説明・単価・金額)」などが含まれます。 スキーマ設定の際は、最終的に会計システムやデータベースで管理しやすい形式を意識するのがポイントです。 もし汎用性の高いデータ定義が必要であれば、schema.orgInvoiceの標準フォーマットも掲載されていますので参考になります。 Parseurでは請求書用の標準データスキーマを用意しており、要件に合わせて請求書メールボックスのフィールド名を変更することで、こちらで説明しているようにユースケースに合わせてカスタマイズすることも可能です。データフォーマットが定義されたら、請求書を画像からテキストに変換できます。

例えば、請求書用のデータスキーマを下記のようなJSON(Swagger形式)で表すことが出来ます:

{
    "InvoiceNumber": {
        "type": "string",
        "description": "The invoice number"
    },
    "InvoiceIssueDate": {
        "type": "string",
        "description": "The invoice date"
    },
    "Items": {
        "type": "array",
        "description": "The list of items in the invoice",
        "items": {
            "type": "object",
            "properties": {
                "quantity": {
                    "type": "number",
                    "description": "The quantity of the item"
                },
                "description": {
                    "type": "string",
                    "description": "The description of the item"
                },
                "unit_price": {
                    "type": "number",
                    "description": "The unit price of the item"
                },
                "price": {
                    "type": "number",
                    "description": "The total price of the item"
                }
            }
        }
    }
}

解析コードを書く前に、これを書き留めてください。これは、以下のすべてのメソッドが満たさなければならない契約であり、後でビジョンモデルに渡すものとなります。

請求書を画像からテキストに変換する

A screen capture of an invoice taken from a smartphone
Picture of an invoice taken from a smartphone

PDFファイルにはスキャン画像が含まれている場合があります。たとえば、社員がスマートフォンで請求書を撮影してPDFにし、経理担当者へ送るケースです。 経理担当者はこの請求書からミスなく情報を抜き出し、会計システムに手入力しなければなりません。 このような画像からデータを抽出するには、光学文字認識 (OCR)を使いテキスト化する必要があります。 有名なOCRとしてTesseractがあります。TesseractはC/C++で記述されていますが、Pythonから利用するにはPyTesseractなどのバインディングが必要です。バインディングとは、他言語(この場合Python)からライブラリ(Tesseract)を呼び出す仕組みのことです。 他にも多くのOCRエンジンがあり、その結果は使われる技術やドキュメントのスキャン品質によって大きく異なります。 Parseurは文書が画像かどうかを自動判別し、内部で自動的にテキスト化します。文書のデータがテキスト化されれば、抽出の準備は完了です。

データスキーマに従って請求書内のテキストを抽出する

PDFがテキスト(検索可能)になっていれば、pdftotextというPythonライブラリでPDFからテキストデータを取得できます。 以下は、PDFからテキストを抽出する基本的なサンプルコードです:

import pdftotext

# 請求書を読み込む
with open("invoice.pdf", "rb") as file_handle:
    pdf = pdftotext.PDF(file_handle)

# すべてのページをループ処理する
for page in pdf:
    print(page)

このスクリプトを convert_pdf_to_text.py として保存し実行すると、標準出力にPDFのテキストが表示されます。 テキストをファイルに書き出したい場合は下記コマンドを利用します:

$ python convert_pdf_to_text.py > invoice.txt

pdftotextはフラットな文字列を返します。これはヘッダーフィールドには適していますが、テーブルには絶望的です。明細行が必要な場合は、すべての単語の座標を保持し、テーブル自体の抽出を試みることができるpdfplumberを使用してください。

import pdfplumber

with pdfplumber.open("invoice.pdf") as pdf:
    page = pdf.pages[0]

    # ページ上の位置を含む単語
    for word in page.extract_words():
        print(word["text"], word["x0"], word["top"])

    # そして明細行テーブルの抽出を試みる
    table = page.extract_table()
    if table:
        for row in table:
            print(row)

実際のサプライヤーの請求書でこれを実行すると、非常に多くの場合、tableNoneになることがわかります。これはバグではありません。この問題が見た目よりも難しいという最初の正直なシグナルであり、これについては後で詳しく説明します。

テキスト形式の請求書が手に入ったので、以下の手法の組み合わせを使用して、そこから必要なデータを抽出できます:

  • 正規表現でのパターン抽出。強力ですがフォーマット変更に弱く、表形式データには向きません。
  • 動的OCRゾーンOCRなど視覚的なテンプレート解析。強固ですが、実装にはより高度な設定が必要です。
  • スキーマとともにページをビジョンモデルに渡し、人間が文書を読むのと同じように読み取らせることができます。これは過去2年間で状況を変えたアプローチであり、後ほど独自のセクションで説明します。

Python標準の re モジュールで請求書番号を抽出するサンプル:

import re

# 請求書を読み込む
with open("invoice.txt", "r") as file_handle:
    invoice = file_handle.read()

# 請求書番号を抽出する
invoice_number = re.search(r"Invoice number: (\w+)", invoice).group(1)
print(invoice_number)

このスクリプトを extract.py に保存し実行すると、請求書番号が出力されます:

$ python extract.py

例として、

INV-1234

が表示されます。

サプライヤーがレイアウトを変更すると、正規表現による請求書パーサーが壊れるのはなぜか

正規表現は文字列と一致しますが、請求書は画像です。うまくいかない問題のすべてはその不一致から生じており、それは予測可能な少数のパターンで失敗します。

  • ラベルが移動した。 パターンはInvoice number:に固定されていますが、新しいテンプレートではInvoice #となっているか、同じ行ではなく次の行に値が配置されています。
  • extract_tableNoneを返す。 テーブルエクストラクターは罫線を探します。ほとんどのサプライヤーの請求書は空白で列を揃えており、境界線はまったく引かれていません。
  • テキストが間違った順序で出力される。 ページが文字列に平坦化されると、2列のレイアウトやフローティングアドレスブロックが混在し、明細行がシャッフルされて到着します。
  • テーブルが複数のページにまたがっている。 2行目から9行目は1ページ目にあり、10行目から14行目は2ページ目にあり、その間に列ヘッダーが繰り返され、小計行が明細行のように表示されます。
  • 3つの数字がすべて合計のように見える。 小計、合計、請求額、および繰越残高。クレジット(マイナス請求)が含まれている請求書で最大の数値を選ぶと、間違いになります。
  • スキャンが写真である。 OCRがかすれた83として読み取っても、3は完全に有効な数字であるため、下流のプロセスでは何も気付きません。
  • 結合されたセルと複数行の記述。 1つの製品説明が3行に折り返され、行分割ロジックによって価格のない3つのアイテムに分割されてしまいます。

これらはどれも、より優れた正規表現で修正できるものではありません。これらはすべて、文字列の問題を装ったレイアウトの問題なのです。これが、ほとんどの人が際限なく増え続けるサプライヤーごとのテンプレートを書き始めるか、アプローチを変えるかの分岐点となります。

2026年のアプローチ - ビジョンモデルとスキーマ

大きな変化は、抽出する前にページをテキストに平坦化する必要がなくなったことです。ビジョンモデルはレンダリングされた請求書を見るため、サプライヤーが請求書番号を移動させても問題にはなりません。あなたが提供するのはパターンではなく、この記事の冒頭で作成したスキーマです。

毎回同じキーを取得できるように出力を制約します。OpenAIの構造化出力のような構造化出力モードとPydanticの組み合わせが、それを実現してくれます。

from typing import List
from pydantic import BaseModel

class LineItem(BaseModel):
    description: str
    quantity: float
    unit_price: float
    amount: float

class Invoice(BaseModel):
    vendor_name: str
    invoice_number: str
    invoice_date: str      # ISO 8601
    currency: str
    subtotal: float
    tax: float
    total: float
    line_items: List[LineItem]

# PDFページを画像としてレンダリングし、ビジョンモデルに送信して、
# 応答がInvoiceスキーマに一致することを要求する。
# モデルはフィールドに入力する。形を発明することはできない。

これにより、抽出問題の大部分が純粋に解決され、このアプローチがこれほど急速に広まった理由でもあります。しかし同時に、正規表現にはなかった新たな失敗モードも生み出します。請求書番号を見つけられない正規表現はNoneを返しますが、それを見つけられないAIモデルは、時としてもっともらしい番号を作り出してしまうのです。安全を保つためのルールはシンプルです。「モデルが提案し、あなたのコードが検証する」ことです。

自分でモデルを実行したくない場合は、Azure AI Document IntelligenceAmazon Textract's AnalyzeExpenseなど、クラウドプロバイダーが提供するマネージドサービスとしても同じ機能が販売されており、どちらもヘッダーフィールドと明細行を別々に返します。この根本的なアプローチがルールベースの解析とどのように異なるのかについてはAI対ルールベースのPDFパーサーで解説し、請求書に特化した場合にどのようになるかについてはビジョンAIによる請求書処理で説明しています。

自分で書く必要があるバリデーションレイヤー

何がJSONを生成したにせよ、これらのチェックはエクストラクターの信頼度スコアではなく、独自のコードで行うべきです。これらは安価で決定的であり、金銭的損失につながるエラーを捕捉します。

  1. 小計 + 税金 + 送料 - 割引合計と1セント(1円)の範囲内で一致する。
  2. 明細行の金額の合計が小計と一致する。一致しない場合は、行を見落としたか、存在しない行を作り出しています。
  3. すべての数量 * 単価がそれぞれの金額と等しい。
  4. 日付が解析可能であり、未来の日付ではない。
  5. そのベンダーに対して、請求書番号の支払いがまだ行われていない。重複支払いは、買掛金において最も高くつくバグです。
  6. ベンダー名が、ベンダーマスターのレコードと解決(一致)できる。
  7. 支払先の銀行口座情報が、そのベンダーに対してすでに登録されているものと一致する。ここでの変更はデータチェックではなく、不正チェックとなります。
  8. 通貨が実際に取引を行っているものである。
  9. 発注書番号が存在し、2ウェイまたは3ウェイマッチングを実行する場合、その数量と価格が一致する。
  10. 必須フィールドがすべて存在し、空ではない。

失敗したものはすべて、元帳ではなく人間による確認に回されます。

def validate(invoice):
    errors = []

    if abs(invoice.subtotal + invoice.tax - invoice.total) > 0.01:
        errors.append("totals_do_not_add_up")

    line_sum = sum(item.amount for item in invoice.line_items)
    if invoice.line_items and abs(line_sum - invoice.subtotal) > 0.01:
        errors.append("line_items_do_not_sum_to_subtotal")

    if not invoice.invoice_number:
        errors.append("missing_invoice_number")

    return errors

10行の算術計算は、いくらプロンプトを調整するよりも、より多くの現実的な問題を捕捉します。

invoice2dataや他のライブラリについてはどうか?

invoice2dataは、多くの人が最初にたどり着く結果であり、純粋に優れたソフトウェアであるため、ここで正当に言及する価値があります。これは、サプライヤーごとに記述したYAMLテンプレートと請求書を照合するコマンドラインツールおよびPythonライブラリであり、マッチングロジックはコードに埋もれることなくバージョン管理下に置かれます。レイアウトを絶対に変更しないサプライヤーが十数社いる場合、それは長年にわたって役立つでしょう。

限界は品質ではなく、その設計にあります。サプライヤーごとに1つのテンプレートが必要ということは、サプライヤーリストとともにメンテナンスが増加し、まさに上記で挙げた理由によってテンプレートが壊れることを意味します。アクティブなレイアウトが20〜30のどこかを超えた時点で、テンプレートを保守する担当者は、手入力で請求書を打ち込んでいた担当者よりも多くの作業を行うことになります。

同じ推論は、より幅広いライブラリにも当てはまります。pdfplumber、PyMuPDF、pdftotext、pytesseractはいずれも、ページから文字と座標を取得するという実際の仕事において非常に優れています。しかし、どれが合計金額であるかを把握するように作られているものは一つもありません。私たちは、最高のPDFパーサー最高のデータ抽出APIのまとめで、より広範なツール群を比較しています。

抽出したデータを収集する

Pythonを活用することで、指定フォルダ内の複数の請求書PDFからデータを一括して抽出できます。 たとえば請求書番号と合計金額を抜き出してCSVとして出力するには次のようにします:

import os
import re

import pdftotext

# フォルダ内のすべてのPDFファイルをループ処理する
for filename in os.listdir("invoices/"):
    if not filename.endswith(".pdf"):
        continue

    # 請求書を読み込む
    with open("invoices/" + filename, "rb") as file_handle:
        pdf = pdftotext.PDF(file_handle)

    # CSVの列ヘッダーを出力する
    print("InvoiceNumber,TotalAmount")

    # すべてのページをループ処理する
    for page in pdf:
        # 請求書番号を抽出する
        invoice_number = re.search(r"Invoice number: (\w+)", page).group(1)
        total_amount = re.search(r"Total amount: (\w+)", page).group(1)
        print(invoice_number, total_amount, sep=",")

このスクリプトを extract_to_csv.py で保存・実行すると、請求書番号と合計金額が標準出力され、それをCSV形式でリダイレクトすればExcel等のスプレッドシートソフトウェアで活用できます:

$ python extract_to_csv.py > invoices.csv

ほとんどの請求書スクリプトはCSVファイルのフォルダを生成したところで終了しますが、本当の会計業務はそこから始まります。誰かがそれらをインポートし、ベンダーを照合し、会計システムが拒否した行を追いかけ、午前2時に検証に失敗した請求書の扱いを決定しなければなりません。その作業はスクリプトには現れず、トークンの請求書にも現れません。

自社構築をやめるべきタイミング

これは多くのベンダーがスキップする部分なので、最初に言っておきます。安定した少数のサプライヤーがいて、パイプラインを監視できるエンジニアがおり、データの処理を待っている人が誰もいない場合は、スクリプトを書いてください。ビジョンモデルとスキーマ、そして上記の10行のバリデーションを組み合わせれば、非常にわずかなコストで多くの成果を得ることができ、すべての部分を自分で理解することができます。

自社構築のツケは後からやってきます。そしてそれは抽出部分ではありません。誰もプロトタイプを作らない次のような部分に現れます。

  • 例外処理キュー。 AP(買掛金管理)チームには、フィールドをクリックすると請求書上の該当箇所がハイライトされ、40秒ではなく4秒で修正できる画面が必要です。これはスクリプトではなく、プロダクトです。
  • ベンダーマッチング。 「ACME Ltd」、「Acme Limited」、「ACME LTD.」は1つのサプライヤーですが、元帳は3つを受け入れません。
  • 重複の検出。 全く同じ請求書が火曜日にメールの添付ファイルとして到着し、金曜日に明細書のPDFとして到着します。
  • ステートマシン。 キュー、再試行、部分的な失敗、そして昨夜の300枚の請求書のうち、実際にどれが通過したかを把握することです。
  • 監査証跡。 何が抽出され、人間が何を変更し、誰が承認し、いつ行われたのか。経理部門は通常、監査中にそれを尋ねてきます。
  • JSONの後のすべて。 会計システムへのフィールドマッピング、GL(総勘定元帳)コーディング、発注書との照合、そして拒否された行の処理。

自社構築よりも購入すべき時期が来たことを示す明確なシグナルは以下の通りです。アクティブなサプライヤーのレイアウトが約20〜30を超えている。ヘッダーフィールドだけでなく明細行も必要である。少なからぬ請求書がスキャン画像として到着している。エンジニアリングチームではなく、AP(買掛金管理)チームがミスを修正する必要がある。抽出の失敗により支払いが遅延している。あるいは、最も一般的なケースとして、パーサーを保守している担当者が、他の業務を一切進められなくなっている。

Parseurはどのように処理するか

Parseurは抽出ステップだけでなく、ループ全体を実行するドキュメントパーサーです。請求書は専用のメールボックスアドレス、API、または監視対象のフォルダから届きます。ビジョンAIエンジンはPDF、スキャン画像、写真を読み取り、テキストAIエンジンはメールとテキストドキュメントを読み取ります。フィールドは名前と型が付けられて出力され、明細行は行として出力されます。記述するテンプレートはなく、サプライヤーが請求書のデザインを変更してもメンテナンスするものは何もありません。

JSONの上に得られるのは、この記事が警告してきた半分の作業です。抽出されたデータは所定の場所で確認および修正できるため、AP担当者はチケットを開くことなく誤って読み取られた合計金額を修正できます。修正は抽出エンジンにフィードバックされます。そしてデータは、直接のWebhook統合MakeZapier、またはMicrosoft Power Automateを通じて、あるいはAPIからのJSONとして直接、必要な場所に送信されます。

デフォルトで請求書から抽出するフィールドセットを確認したい場合は、請求書のOCRページにドキュメント化されており、より広範なワークフローについては請求書データキャプチャでカバーされています。

無料アカウントを作成
Parseurで時間と労力を節約。ドキュメント処理を自動化しましょう。

まとめ

Pythonを使用してPDFから請求書データを抽出することは、約100枚の請求書にとっては解決済みの問題ですが、1万枚にとっては未解決の問題です。この2つの数字の間で変わるのはコードではありません。変わるのは、どれだけのサプライヤーのレイアウトをあなたが静かに保守し続けることになるか、そしてそのうちの1つが変更されたときに誰が呼び出されるかということです。

スクリプトを書いてみてください。上記の7つの失敗モードのうち、どれに最初に直面するかを正確に知るためだけでも、一度は実行する価値があります。その後、あなたの次の6ヶ月間の時間を8番目の問題に費やすのが最善かどうかを正直に判断してください。答えが「ノー」である場合、Parseurは2016年からこの課題に取り組んでおり、あなたからそのフォルダを引き取ります。

最終更新日

さらに詳しく

こちらもおすすめ

今すぐ始める

ドキュメントデータ抽出、
そろそろ自動化しませんか?

数分で設定完了。Parseurがどう業務フローに収まるか、無料でお試しいただけます。

AIモデルの学習は不要
あらゆるドキュメントからのデータ入力を自動化
クリック操作からAPIまで柔軟に対応

よくある質問

最初の請求書スクリプトが稼働し、2社目のサプライヤーのフォーマットでそれが壊れた後に、開発者が実際に尋ねる質問です。

pdfplumberやpdftotextのようなライブラリを使用してPDFからテキストを読み取り、そのテキストから指定したフィールドを抽出します。デジタルのまま生成されたPDFの場合、これは2段階の作業になります。スキャンの場合、画像をテキストに変換するために、まずOCR処理が必要です。本番環境で機能するかどうかを決定する部分は、読み取りそのものではなく、サプライヤーごとにレイアウトが異なる場合に、テキストの壁から請求書番号、日付、ベンダー、明細行をどのように取得するかという点です。

invoice2dataは、サプライヤーごとに記述するYAMLテンプレートを使用して請求書からフィールドを抽出する、オープンソースのコマンドラインツールおよびPythonライブラリです。少数の安定したサプライヤーがおり、マッチングロジックをコード内ではなくバージョン管理下に置きたい場合に適しています。サプライヤーごとに1つのテンプレートを作成して維持するコストが、手入力による手間の削減効果を上回った時点で、適切な選択肢ではなくなります。

明細行のテーブルはヘッダーフィールドとは別の抽出として扱い、解析する前にページをまたいで再構築します。pdfplumberの extract_table は、罫線のあるきれいなテーブルでは機能しますが、境界線がないテーブル(ほとんどのサプライヤーの請求書が該当します)では何も返しません。信頼できるパターンは、サプライヤーのレイアウトごとに列の境界を1回検出し、改行を越えてそれらを引き継ぎ、繰り返されるヘッダー行を削除してから、抽出した行の合計が小計と一致するかどうかを確認することです。一致しない場合、行を見落としています。

エクストラクターの信頼度スコアだけに頼るのではなく、独自のコードによる決定論的なチェックで行います。中核となるセットは、算術とIDです。小計に税金と送料を足して割引を引いた額が合計と一致するか、明細行の合計が小計と一致するか、日付が解析可能で未来ではないか、そのベンダーに対してその請求書番号がすでに支払われていないか、ベンダーがベンダーマスターに存在するか、通貨が予想されるものであるかを確認します。失敗したものはすべて、元帳ではなく人間による確認に回されます。

抽出が最も難しい部分ではなくなった時です。ビジョンモデルの呼び出しは安価で、プロトタイプを素早く作成できるため、少数の安定したサプライヤーとパイプラインを監視できる人がいる場合は、構築してください。請求書解析以外にかかるコストは後からやってきます。キューと再試行ロジック、APチームが必要とする例外確認画面、重複検出、ベンダーマッチング、監査証跡、会計システムへの統合などです。ページあたりの価格を比較する前に、これらを数えてください。

はい。ただし、スキャンしたPDFはテキストライブラリが読み取れるようにする前に、OCRパスが必要です。写真やスキャンされた請求書はPDFでラップされた画像であるため、pdfplumberやpdftotextは空の文字列を返します。通常のオープンソースルートは、画像を傾き補正してクリーンアップした後、pytesseractバインディングを介してTesseractを使用します。最新のビジョンモデルはその手順を完全にスキップし、画像を直接読み取ります。

請求書専用のライブラリはなく、PDFライブラリと独自のロジックを組み合わせるしかありません。pdfplumberは単語をその座標とともに公開し、テーブル抽出機能を備えているため、通常は最初の選択肢となります。大量のバッチ処理ではPyMuPDFの方が高速です。pdftotextは、生のテキストだけが必要な場合に機能する最小のものです。pytesseractはスキャンを処理します。それぞれがテキストまたはボックスを提供しますが、どれが合計金額であるかを教えてくれるものはありません。

正規表現は文字列と一致しますが、請求書は画像だからです。パターンは、サプライヤーの新しいテンプレートによって移動されたラベル、改行、または列の位置に固定されています。請求書は半構造化された視覚的なドキュメントであるため、システムを壊す原因は文字列の問題ではなくレイアウトの問題です。ページをまたいで分割されたテーブル、繰り返されるヘッダー、結合されたセル、小計、合計、請求額の違いは、優れた正規表現では修正できません。

はい。現在ではそれがPDFから構造化されたJSONへの最短ルートですが、スキーマとその周囲のバリデーターがある場合のみです。ビジョンモデルは人間と同じように請求書を読み取るため、サプライヤーがレイアウトを変更しても問題にはなりません。OpenAIの構造化出力Pydanticモデルなどを使用してJSONスキーマで出力を制約し、毎回同じキーを取得できるようにします。その後、自分で計算を確認してください。請求書番号を見つけられないモデルは、もっともらしい番号を生成することがあります。

精度は製品ではなくフィールドとスキャンに依存するため、単一の見出しのパーセンテージはマーケティング用語として扱ってください。きれいなデジタル生成のPDFに印刷された合計金額と請求書番号は、ほぼ完璧に返されます。写真に撮られた、歪んだ、コントラストの低いスキャンの同じフィールドでは、数字が混同されやすく、間違った文字が実際の金銭的損失につながる可能性があります。測定すべき数字は文字の精度ではなく、人間が触れることなく会計システムに到達する請求書の数です。

サプライヤーごとにコードや設定を書くのをやめることです。各ベンダーのテンプレート、正規表現セット、または座標マップを必要とするアプローチは、サプライヤーのリストとともに直線的に増大し、アクティブなレイアウトが20〜30を超えると、誰の作業量も減らしません。ドキュメントを視覚的に読み取るモデルは、設計上フォーマットに依存しないため、レイアウトの変更がメンテナンスイベントではなくなります。依然として注意が必要なのは例外キューであり、それは抽出の問題ではなくワークフローの問題です。

CSVの書き込みは数行のPythonコードで済み、簡単な半分の作業です。難しい半分の作業は、請求書の支払いを行うシステムにデータを入力することです。つまり、フィールド名をそのシステムのスキーマにマッピングし、ベンダーを既存のレコードと照合し、拒否された行を処理し、失敗した呼び出しを再試行します。誰かが手作業でインポートするCSVファイルのフォルダでは誰の午後の時間も節約できないため、最初からその半分の作業を計画してください。