見積書PDFから単価表をCSV一括抽出するPythonスクリプト【pdfplumber】

  • URLをコピーしました!

協力会社や物品購入で受け取る見積書、無料でCSVへデータベース化する方法を考えてみました。

取引が多い日は10社分・20社分など、それを手入力すると半日潰れてしまうと思います。
しかも金額の転記ミスというヒューマンエラーは常につきまといます。

ではそこに設備投入できるかといえば、全ての事業者ができるわけではありません。

今回はPythonスクリプトで、フォルダ内の見積書PDFを一括読み込みし、品目・規格・数量・単位・単価をCSVに自動出力する方法を紹介します。

このスクリプトが使えるのは「テキスト埋め込みPDF」に限られます。
下図のようにマウスでテキストを選択できるデータ形式が埋め込みPDFです。

目次

コードの仕組み(3行で説明)

  1. pdfplumber が罫線を自動認識してセルの値を取り出す
  2. find_col() でヘッダー行から「品目」「単価」などの列番号を特定する
  3. 各行の値を取り出してCSVに保存する(合計行・空行は自動スキップ)
つかだ

会社ごとに異なる書式に対応できるコードづくりにしました!PDFによってヘッダー名が違っても、HINMOKU_KEYS などのリストにキーワードを追加するだけで対応できます!

こんな方に向けて書いています

  • 建設・設備・不動産会社で積算・原価管理・発注管理をしている
  • 協力業者・下請けからもらうPDFの単価を台帳や見積比較表に転記する作業がある
  • 見積書のデータベース化を無料でしたい
  • Pythonは詳しくないが、コピペして動かすくらいはできる

最初に確認:対応しているPDFの種類

このスクリプトが使えるのは「テキスト埋め込みPDF」に限られます。

種類作り方の例このスクリプトで読めるか
テキスト埋め込みPDFWord・Excel・会計ソフト・建設ソフトから「PDF保存」したもの✅ 読める
スキャン画像PDF紙をコピー機でスキャンしたもの❌ 読めない

判断方法は簡単です。PDFをブラウザやAcrobat Readerで開いて、文字の上をクリックしてみてください。文字が選択できれば「テキスト埋め込みPDF」です。何も反応しなければスキャン画像PDFなので、このスクリプトでは対応できません。

準備:ライブラリのインストール

Pythonがまだインストールされていない方は、Python公式サイトから 3.10 以上をダウンロードしてインストールしてください。インストール時に「Add Python to PATH」のチェックを必ず入れることを忘れずに。

動作確認環境

ライブラリバージョン
Python3.13.5
pdfplumber0.11.9
pandas3.0.2
Windows11

ライブラリのインストール

pip install pdfplumber pandas

pdfplumberで見積書をデータベース化する

PDF内の表のリスト格納

pdfplumberへ取得したい見積書PDFを読ませます。
下記のコードでは、まずリストtablesにPDF内全ての表を格納します。

import pdfplumber

# ── PDFの指定と表取得 ──────────────────────────────────────────────────────
pdf_path = "見積書.pdf" # 取得したいPDFのパス

with pdfplumber.open(pdf_path) as pdf:
    for page in pdf.pages: # 1ページごとループ処理
        tables = page.extract_tables() # ← ここでページ内の表を認識し、リストtablesへ格納

extract_tables() が罫線を検出してセルの2次元リストを返します。
PDF内の表の形をしているものは、すべてリストtablesに格納されます。

このサンプル見積書の場合、リストtablesへ2種類の表が格納されます。

見積もり内訳の書式を満たす表を特定する

リストtablesには、上記のようなPDFでは表の情報が複数含まれます。
この取得した表情報のどれが目的の見積り内訳の表なのかを判別する必要があります。

判別の手法としては、「ヘッダー」が内訳の書式であるかを基準とします
表の1行目に「品目」や「単価」などの記載があれば、その表が見積り内訳であると判別します。
ヘッダーの各列の判別基準はリスト化しており、その基準は編集可能な作りにしてみました。

# ── 表特定の判定基準と取得項目の設定 ──────────────────────────────────────────────────────
HINMOKU_KEYS = ["品目", "品名", "工種", "工事名", "item"] #ヘッダー品目列の判別基準
TANKA_KEYS   = ["単価", "unit price"] #ヘッダー単価列の判別基準
KIKAKU_KEYS  = ["規格", "仕様", "型番", "spec"] #ヘッダー規格列の判別基準
TANI_KEYS    = ["単位", "unit"] #ヘッダー単位列の判別基準
SURYO_KEYS   = ["数量", "qty", "quantity"] #ヘッダー数量列の判別基準

# ── ヘッダーの取得と見積り書式の判定 ──────────────────────────────────────────────────────
for table in tables:
    header = table[0] # 表の一行目 ヘッダーのみリストで取り出す
    hi = find_col(header, HINMOKU_KEYS) # HINMOKU_KEYSに該当する値があれば、列数を格納
    ti = find_col(header, TANKA_KEYS) # TANKA_KEYSに該当する値があれば、列数を格納
    ki = find_col(header, KIKAKU_KEYS) # KIKAKU_KEYS
    ui = find_col(header, TANI_KEYS) # TANI_KEYS
    si = find_col(header, SURYO_KEYS) # SURYO_KEYS

    if hi is None or ti is None: # hiまたはtiに何も格納されていなければ、見積もり内訳ではないと判断
        continue # 次のtableへ、ここにひっかかからなければ見積り内訳と判断し次のコードへ

この段階で、見積り内訳の表を特定する事は完了しています。
上図と同じ並びでtableリスト内に2次元リスト形式で各セルの文字列が格納されており、
アウトプットする順番がこのままで良ければ、これをcsvとして完了とすることも出来ます。

今回は複数の書式の異なる会社の見積もりを同じ書式でデータベース化する想定ですので、
列の順番を指定して出力する方法を記載します。

つかだ

カスタム性を重視したコードになったと思います😅
他に取得したい要素があれば、判定基準や取得する値を設定すると自由にカスタムできます!

見積り内訳から内容をリストで取り出す

見積り内訳の表リストを特定したら、その表の2行目から行ごとに値を希望の列の順番に並び替えて統一した書式のリストに編纂します。

rows = [] # csv化する値を格納する空リストの宣言

# ── 見積り内訳表の内訳の取得 ──────────────────────────────────────────────────────
for row in table[1:]:          # 表の2行目以降(データ行)をループ
    hinmoku = row[hi]          # 品目列の値 hiには「品目」「品名」などのヘッダーの列数
    tanka   = row[ti]          # 単価列の値
  kikaku  = row[ki]          # 規格列の値
  tani    = row[ui]          # 単位列の値
  suryo   = row[si]          # 数量列の値

# ── 品名が空欄の行や合計行だった場合(表の終わりの認識) ──────────────────────────────────────────────────────
    if not hinmoku or hinmoku in ("", "None"):  # 品目が空欄の場合表の取得を終了
        continue
    if any(kw in hinmoku for kw in ["小計", "合計", "消費税"]): # 品目が合計行の場合表の取得を終了
        continue

# ── 見積り内訳表の内訳の取得 ──────────────────────────────────────────────────────
# 品目が上記以外の文字列だった場合、取得した値を2次元リストに格納する
    rows.append({
        "品目":  re.sub(r"\s+", "", hinmoku),
        "規格":  kikaku,
        "数量":  suryo,
        "単位":  tani,
        "単価":  tanka.replace("\\", "¥"),
  })
  result = pd.DataFrame(rows)          # 辞書のリストを表形式に変換
  result.to_csv("tanka_output.csv",    # CSVファイルに保存
      index=False,           # 行番号は不要
      encoding="utf-8-sig")  # Excelで開いたとき文字化けしない形式
  

rowsは下記のような辞書を内包したリストになります。

[
{“品目”: “路盤工”, “規格”: “再生砕石C-40”, “数量”: “25”, “単位”: “m³”, “単価”: “¥4,200”},
{“品目”: “アスファルト”, “規格”: “密粒13mm”, “数量”: “120”,”単位”: “㎡”, “単価”: “¥3,800”},
{“品目”: “路肩処理”, “規格”: “縁石設置”, “数量”: “30”, “単位”: “m”, “単価”: “¥2,500”}
]

tanka_output.csvでは下記のようなデータが格納されます。

品目規格数量単位単価
路盤工再生砕石C-4025¥4,200
アスファルト密粒13mm120¥3,800
路肩処理縁石設置30m¥2,500
つかだ

見積りの内訳をデータベース化できました。
最近は物価高騰も激しいので、特定の製品の価格情報を時系列に整理などの利用ができると思います!

実際の実行結果

5種類の見積書PDF(道路・解体・設備機器・造園・塗装)で実行し、これらを1つのCSVとして保存してみました。PDFが格納された1つのフォルダから連続取得するように設定を変更しています。

出力されたCSVの一部です。m³・㎡・人日・箇所・セットなど、すべて文字化けなしで正確に取得できています。

品目規格数量単位単価
路盤工再生砕石C-40 t=15cm25¥4,200
アスファルト舗装密粒13mm t=5cm120¥3,800
H形鋼加工SS400 H-300×1508,500kg¥185
溶接工事完全溶込み溶接1¥380,000
コンセント増設2口 接地付き12箇所¥8,500
フローリング複合フロア 12mm t75¥8,500
つかだ

サンプル見積りは内訳ではない表を他種含ませて見ました。また、列も入れ替えたデータで実験しました。
結果、表のヘッダーを認識して正確に見積り内訳表を特定し、こちらの希望する順番に整列することが出来ていました!

うまくいかないときのチェックリスト

0件しか取れない

まずPDFの種類を確認してください。ブラウザで開いて文字をクリックしても選択できない場合は、スキャン画像PDFです。このスクリプトでは読めません。

テキスト埋め込みPDFなのに0件の場合は、ヘッダーの列名が想定外の書き方になっている可能性があります。HINMOKU_KEYSTANKA_KEYS にその列名を追加してみてください。

CSVをExcelで開いたら文字化けする

Excelのメニューから「データ」→「テキストまたはCSVから」でインポートして、文字コードに「UTF-8」を指定してください。

合計金額の行まで取れてしまう

スキップリストに合計金額のキーワードを追加してください。
if any(kw in hinmoku for kw in ["小計", "合計", "消費税"]):

紙の見積書やスキャンPDFが届いた場合は?

「テキスト埋め込みで送ってほしい」と取引先にお願いするのが一番の解決策です。

どうしてもスキャンPDFしかない場合はOCR(文字認識)を組み合わせる方法もありますが、㎡・m³・人日など建設業特有の単位記号が正確に読み取れないことが多く、精度に限界があります。

つかだ

筆者が実際に検証した結果、単位列の正解率は最善の条件でも30%前後でした。「㎡」「kg」などの建設業頻出の環境依存記号がOCRはうまく認識出来ませんでした。
実は最初こちらの検証をしていたのですが、実用性の点から文字埋め込み版の記事に切り替えました😅

まとめ

  • m³・㎡・人日・セット・箇所もすべて正確に取得
  • ヘッダー名が違う書式にもキーワード追加で対応可能
  • 異なる取引先の書式を共通のフォーマットでcsv化でき、他の分析ソフトに共有しやすい

高性能のOCRソフトやOCRつきスキャナーなどが出てきていますが、規模の大きくない事業に導入するにはコスパが悪いというイメージです。
このプログラムはPDF読み取り先をフォルダに設定し、格納PDFを一括でCSV化させ、溜まった見積書を一括でCSVにすることもできます!

つかだ

PDFをフォルダ格納→ソフトを起動→csv化というのは有料ソフトと同じ操作です。浮いたコストと時間で付加価値ある業務に専念できます!

もしご興味ある方がいましたら、是非お知らせください!

よかったらシェアしてね!
  • URLをコピーしました!

北海道在住
現在建設系の事務方として勤務
建設の経験とプロラミングで業務効率化の情報を発信します!
好きな食べ物:揚げたてのLチキ

コメントお待ちしてます!

コメントする

CAPTCHA



reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

目次