PythonでPDFの請求書から金額・日付を抽出してみた
目次
はじめに
初めまして。皆様は、PDFからデータ抽出を行う際は何を使いますか?手動でデータ抽出をされている方もいらっしゃるのではないでしょうか?
データ量が多い場合、1ページずつ目視で確認して必要な部分を抽出するのは非常に時間がかかってしまいますよね。
今回は、Pythonを利用してpdfの請求書から金額と日付を抽出するツールを作成してみたので興味を持っていただけた方は是非ご一読ください!
Pythonを使ったPDFからのデータ抽出方法
では、早速Pythonを利用して請求書から支払金額と支払期日を抽出するツールを作成してみましょう!
Pythonの導入方法については、過去の記事で説明されているのでよろしければ参考にしてください。
~VBAマクロ、もう全部Pythonに任せてみませんか UiPath×Pythonでやってみた~
https://rpa.bigtreetc.com/column/vba2python_with_uipath/
請求書①
請求書はChat GPTを利用して架空の請求書を2個用意してもらいました。1つ目の請求書(Fake_Invoice.pdf)は以下の通りです。
こちらの請求書では、支払金額は”金額:”の右に記載され、支払期日は”支払期日:”の右に記載されています。これらの金額と、支払期日を抽出するためのコードは以下のように作成してみました。
import pdfplumber
import re
# PDFからテキストを抽出する関数
def extract_text_from_pdf(pdf_path):
text = ""
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
text += page.extract_text() # 各ページのテキストを取得
return text
# 金額と日付を抽出する関数
def extract_amounts_and_dates(text):
# 金額の正規表現パターン
amount_pattern = r'¥\s?(\d{1,3}(?:,\d{3})*)'
# 日付の正規表現パターン
date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
# 請求金額を特定 ("合計" の後に出現する金額)
total_amount = re.search(r'金額:\s*' + amount_pattern, text)
if total_amount:
total_amount = total_amount.group(1)
# 支払期日を特定 ("支払期日" の後に出現する日付)
payment_date = re.search(r'支払期日:\s*' + date_pattern, text)
if payment_date:
payment_date = f"{payment_date.group(1)}-{payment_date.group(2)}-{payment_date.group(3)}"
return total_amount, payment_date
# メイン処理
def main(pdf_path):
# PDFからテキストを抽出
text = extract_text_from_pdf(pdf_path)
# 金額と日付を抽出
total_amount, payment_date = extract_amounts_and_dates(text)
# 結果を表示
print("請求金額:", total_amount)
print("支払期日:", payment_date)
# PDFファイルのパスを指定して実行
pdf_path = "Fake_Invoice.pdf" # 処理するPDFファイルのパスを指定
main(pdf_path)
こちらのファイルを実行すると、
請求金額: 500,000
支払期日: 2024-10-20
という結果を得られました。次に、今回のPythonコードの説明をさせていただきます。
ライブラリ
PythonでPDFファイルを処理するために必要な2つのライブラリをインポートしています。1つ目は”pdfplumber”で、 PDFファイルを開いてテキストを抽出するために使います。2つ目は”re”で、 特定の文字列(今回は金額や日付)をテキスト内から見つけ出すために使います。
extract_text_from_pdf関数
extract_text_from_pdf関数では、PDFファイルからすべてのテキストを抽出して1つの文字列にまとめています。
extract_amounts_and_dates関数
日付と金額は、正規表現を使用して絞っております。具体的には、amount_patternで”¥”の後にカンマ付きの数字の形式となっているものを探します。また、date_patternで”YYYY-MM-DD”形式の日付を探します。
次に、total_amountで”金額:”の後ろがamount_patternのパターンになっている文字列を抽出します。これは、請求書の支払金額が「金額:\○○〇,〇〇〇」の形式で記載されていることを前提としています。
さらに、payment_dateで”支払期日:”の後ろがdate_patternのパターンになっている文字列を抽出します。こちらも、請求書の支払期日が「支払期日:yyyy-mm-dd」の形式で記載されていることを前提としています。
main関数
そして、main関数で全体の処理を行います。extract_text_from_pdf(pdf_path)で、指定されたPDFファイルからすべてのテキストを抽出します。次に、extract_amounts_and_dates(text)で、抽出したテキストから支払金額と支払期日を探します。最後に、print()で、結果をコンソールに表示します。
請求書②
次に、異なる形式の請求書(Fake_Invoice2.pdf)で金額と日付の抽出を行います。
こちらの請求書では、支払金額は”合計:”の右に記載され、支払期日は”支払期日:”の右に記載されています。また、一つのpdfに複数の請求書データがまとめられているため、支払金額や支払期日も複数抽出する必要があります。これらの支払金額と、支払期日を抽出するために先ほどのコードの一部を変更してみました。
import pdfplumber
import re
# PDFからテキストを抽出する関数
def extract_text_from_pdf(pdf_path):
text = ""
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
text += page.extract_text() # 各ページのテキストを取得
return text
# 金額と日付を抽出してペアで返す関数
def extract_amounts_and_dates(text):
# 金額の正規表現パターン
amount_pattern = r'(\d{1,3}(?:,\d{3})*)\s*円'
# 日付の正規表現パターン
date_pattern = r'(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日'
# 複数の金額を抽出
total_amounts = re.findall(r'合計:\s*' + amount_pattern, text)
# 複数の支払期日を抽出
payment_dates = re.findall(r'支払期日:\s*' + date_pattern, text)
# 金額と日付をペアにする
paired_data = []
for amount, (year, month, day) in zip(total_amounts, payment_dates):
payment_date = f"{year}-{month.zfill(2)}-{day.zfill(2)}"
paired_data.append((amount, payment_date))
return paired_data
# メイン処理
def main(pdf_path):
# PDFからテキストを抽出
text = extract_text_from_pdf(pdf_path)
# 金額と日付を抽出してペアにする
paired_data = extract_amounts_and_dates(text)
# 結果を表示
for i, (amount, date) in enumerate(paired_data, start=1):
print(f"支払情報 {i}:")
print(f" 請求金額: {amount} 円")
print(f" 支払期日: {date}")
print("-" * 20)
# PDFファイルのパスを指定して実行
pdf_path = "Fake_Invoice2.pdf" # 処理するPDFファイルのパスを指定
main(pdf_path)
こちらのファイルを実行すると、
支払情報 1:
請求金額: 104,500 円
支払期日: 2024-11-10
--------------------
支払情報 2:
請求金額: 165,000 円
支払期日: 2024-12-10
--------------------
支払情報 3:
請求金額: 143,000 円
支払期日: 2025-01-10
--------------------
という結果が得られました。1つ目の請求書のときと、extract_amounts_and_dates関数とmain関数で変更点があるため、その説明をさせていただきます。
extract_amounts_and_dates関数
まず、金額・日付を抽出する正規表現を今回の請求書の形式に合わせて変更しました。金額の抽出は、amount_patternで”円”という文字列が含まれているものを探しています。日付の抽出に関しては、date_patternで” YYYY年MM月DD日”の形式となっているものを探しています。
また、最初に見つかった1つの結果だけを返すre.search()ではなく、すべての一致する結果をリストとして返すre.findall()を使用しています。そして、支払金額と支払期日をペアにしてpaired_dataに追加しております。
main関数
そして、main関数ではループ処理を使用してすべての支払金額と支払期日を順番に表示するように変更しました。番号を付けて、どの金額とどの支払期日が対応しているかが分かるようにしています。
これら2つの請求書に関しては、正確に支払金額と支払期日の抽出を行うことができました。私自身、業務でPythonを使用した経験はなかったのですが、このような簡易的な金額・日付抽出ツールを作成することはできました。
ただし、今回想定した請求書の形式はpdfであるため、紙の場合はOCRの技術に頼る必要がありそうです。
今回私が使用したPythonで文字列抽出をする場合と、OCRを使用する場合を比較してみました。
対応可能なフォーマット | 精度 | 処理速度 | コスト | |
Python | テキストが抽出可能なPDFやWordに強い | テキストベースのため高精度 | 速い | オープンソースのツールで低コスト |
OCR | 紙の媒体やスキャン画像にも対応 | 誤認識が発生する可能性がある | (画像処理が多い場合)遅い | 高品質なOCRツールはコストがかかる場合がある |
文字列を抽出する場合のフォーマットがデジタルデータの場合はPythonを使用することが可能ですが、紙媒体の文書から文字列抽出をする場合はOCRが必要でしょう。
おわりに
今回はpdfの請求書から金額や日付を抽出してみましたがいかがでしたか?手動で該当の金額や日付を探すよりも、効率的かつミスもなくなるため、自動化のメリットが大きいです!
手動で行っている方がいらっしゃいましたら、請求書の形式に合わせてPythonやOCRの導入を検討されてはいかがでしょうか?
最新情報をお届けします!
RPAに関する最新コラムやイベント情報をメールで配信中です。
RPA領域でお仕事されている方に役立つナレッジになりますので、ぜび登録してください!
- November 2024 (2)
- October 2024 (3)
- September 2024 (2)
- August 2024 (4)
- July 2024 (1)
- June 2024 (2)
- May 2024 (3)
- April 2024 (1)
- March 2024 (1)
- February 2024 (1)
- January 2024 (1)
- December 2023 (1)