第 04 章「処理を書く ── AIにPythonで書いてもらう」の主張を裏付ける。
章のどの主張に対応するか
100 個の請求書 PDF から金額を抽出する月次作業: 手作業で 4 時間。 Claude が書いた Python で 3 秒。翌月も同じスクリプトで 3 秒。4,800 倍の差。
(章本文「実例: 数字で見る」より)
実測 1.0 秒。100 件すべて抽出成功。詳細は results.md。
やること
- 入力を作る: WeasyPrint で 100 件の請求書 PDF(各 A4 1 枚)を生成
- 宛名、明細表、小計・消費税・合計、振込先、フッターまで
- 抽出する: pypdf で各 PDF からテキストを取り出し、正規表現で 請求番号・宛名・合計金額を取る
- 集計する: 全 100 件の合計、顧客別ランキング上位 10 社
- 検算する: 生成時の正解と、抽出結果の合計が一致するか確認
全部 make all で動く。
構成
example-1/
├── README.md
├── generate_invoices.py ── 100 件の PDF を WeasyPrint で生成
├── extract_amounts.py ── pypdf で金額を抽出 → CSV / JSON
├── Makefile
├── results.md
├── pdf/ ── 入力 (100 ファイル, 約 24 MB)
└── out/
├── extracted.csv ── 抽出結果(全 100 件、列: file, invoice_no, customer, total)
├── summary.json ── 集計結果(合計、顧客別ランキング)
└── run.log ── 実行ログ
実行
pip install weasyprint pypdf
make clean && make all
なぜこれが「実例」になるのか
請求書 PDF の金額抽出は、典型的に手作業で残っている事務処理だ。
- PDF を 1 枚ずつ開く
- 合計金額を目で読む
- Excel に書き写す
- 100 件繰り返す
extract_amounts.py の核心は 30 行:
INVOICE_NO_RE = re.compile(r"INV-2026-(\d{4})")
CUSTOMER_RE = re.compile(r"請求先[::]\s*(.+?)\s*御中")
TOTAL_RE = re.compile(r"合計金額[::]?\s*([\d,]+)\s*円")
def parse_pdf(path):
text = "\n".join(p.extract_text() or "" for p in PdfReader(str(path)).pages)
return {
"invoice_no": INVOICE_NO_RE.search(text).group(0),
"customer": CUSTOMER_RE.search(text).group(1),
"total": int(TOTAL_RE.search(text).group(1).replace(",", "")),
}
これを Claude に「100 個の請求書 PDF から合計金額を抽出して、顧客別に集計して」 と頼めば書いてくれる。人間は意図を伝えるだけ。実装は AI に渡る。
そして翌月、新しい 100 件が届く。同じスクリプトを python extract_amounts.py
で実行 ── また 1 秒で終わる。手作業なら毎月 4 時間ずつ消える。
これが章で言う「繰り返しの仕事が、一回限りの仕事になる」の具体形。
計測結果 — 第 04 章 example-1
実行環境: Linux 6.18 / pypdf 6.10 / WeasyPrint 68.1 / Python 3.x
抽出時間(主目的)
=== 100 個の請求書 PDF から金額抽出 ===
処理時間: 1.025 秒 (100 ファイル)
抽出成功: 100 / 100 件
合計金額: 112,714,129 円
| 項目 | 数値 |
|---|---|
| 処理対象 | 100 PDF, 各 A4 1 枚 |
| PDF 合計サイズ | 約 24 MB |
| 処理時間(pypdf による全文抽出 + 正規表現) | 1.025 秒 |
| 抽出成功率 | 100/100 (100%) |
| 抽出した合計金額 | 112,714,129 円 |
| 生成時の正解金額 | 112,714,129 円(完全一致) |
章本文の主張「Python で 3 秒」よりも速かった(1 秒)。 手作業 4 時間 = 14,400 秒との比は 約 14,000 倍。
顧客別ランキング(out/summary.json)
清水運送 10,112,216 円
吉田出版 7,710,720 円
木村電機 7,410,206 円
森精密 7,213,563 円
加藤建設 7,078,098 円
高橋食品 7,072,101 円
斎藤運輸 6,917,039 円
小林技研 6,838,507 円
田中株式会社 6,583,454 円
山田農園 5,829,126 円
抽出結果が CSV と JSON で残るので、翌月そのまま再利用できる。
来月も python extract_amounts.py で 1 秒。来年も同じ。
抽出後のデータ(out/extracted.csv 抜粋)
file,invoice_no,customer,total
INV-2026-0001.pdf,INV-2026-0001,鈴木商店,1054720
INV-2026-0002.pdf,INV-2026-0002,森精密,627000
INV-2026-0003.pdf,INV-2026-0003,清水運送,4297540
INV-2026-0004.pdf,INV-2026-0004,池田化学,1031206
...
CSV になれば、後段の処理は何でもできる。
# 顧客別合計を 1 行で
awk -F, 'NR>1 {a[$3]+=$4} END {for (c in a) print c, a[c]}' out/extracted.csv | sort -k2 -n -r
# 100 万円以上の請求だけ
awk -F, 'NR>1 && $4 > 1000000' out/extracted.csv | wc -l
エラーが出たら
extract_amounts.py を Claude が初回で完璧に書く保証はない。たとえば:
TypeError: extract_text() returned None for page 3
このエラーをそのまま Claude に貼ると、p.extract_text() or "" で
None を空文字に置換するコードが返る。それで進む。
章本文の言う「エラー文をそのまま Claude に貼る。Claude が直す。これで進む」 の具体形。
再現手順
pip install weasyprint pypdf
sudo apt install fonts-noto-cjk
make clean && make all
PDF 100 件の生成に 数秒、抽出に 1 秒。