【完全ガイド】Pythonで請求書PDFを自動生成

ソニック

月3時間の業務を10秒へ|事務職の必修テクニック

「毎月100枚の請求書を手作業で作っている」「Excelで作った請求書を1枚ずつPDF化している」

そんな業務、ありませんか?

私自身、データサイエンス業務に転身する前、請求書作成業務に毎月3時間以上かけていました。テンプレートをコピーして、宛先を変えて、金額を計算して、PDFに変換して、保存して……100社分の作業は本当に苦痛でした。

そんな業務を変えたのが、Pythonでの請求書PDF自動生成でした。100枚の請求書が、たった10秒で完成します。

この記事では、Pythonで請求書PDFを自動生成する方法を、コピペで使えるコード付きで完全解説します。

目次

こんな方におすすめ

  • 毎月、複数社の請求書を手作業で作成している事務職・経理職の方
  • Excelで請求書を作ってPDFに変換する作業を効率化したいバックオフィスの方
  • Pythonでの業務自動化に挑戦したい方

第1章|なぜ請求書PDF自動化が業務を変えるのか

請求書業務の現場リアル

毎月の請求書業務、こんな流れではありませんか?

  1. Excelテンプレートを開く
  2. 宛先・件名・明細・金額を入力
  3. 印刷プレビューで確認
  4. PDFに変換
  5. ファイル名を「請求書_顧客名_202606.pdf」に変更
  6. メールに添付して送信

これを毎月、顧客の数だけ繰り返す。50社なら**1社あたり3分でも合計2.5時間**。100社なら5時間。

Before/After|PDF自動生成の威力

業務Before(手作業)After(Python自動化)
100社の請求書作成月3〜5時間10秒
入力ミスのリスク頻発ゼロ
ファイル名の統一人によってバラバラ自動で統一
修正・再発行やり直しデータ修正だけで再生成

第2章|環境準備(uv環境前提)

この記事は、uv環境でPythonを使う前提で進めます。まだuvをインストールしていない方は、別記事「【完全版】uv入門」をご覧ください。

必要なライブラリ

# PDFを作成するためのライブラリと、データ処理用ライブラリをインストール
uv add reportlab pandas openpyxl

各ライブラリの役割

ライブラリ役割
reportlabPDFファイルの直接生成
pandasCSVやExcelの顧客データ読み込み
openpyxlExcelファイル操作(テンプレ用)

日本語フォントの準備

reportlabは標準では日本語を扱えないため、IPAフォントを使います。Windowsには標準で入っている「メイリオ」を使う方法もあります。

# Windowsならメイリオが標準で使える
# C:\Windows\Fonts\meiryo.ttc

# Macなら以下のいずれか
# /System/Library/Fonts/ヒラギノ角ゴシック W3.ttc
# /Library/Fonts/Arial Unicode.ttf

第3章|基本|Pythonで1枚のPDFを作る

まずは「Hello World」級のシンプルなPDFを作ります。これで全体の流れを掴みます。

最小のPDF生成コード

# 必要なモジュールをインポート
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

# === 日本語フォント設定 ===
# Windowsの場合は「メイリオ」
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc"
pdfmetrics.registerFont(TTFont("Meiryo", FONT_PATH))

# === PDF作成 ===
# 出力ファイル名を指定してキャンバスを作成
# A4サイズで作成(縦向き)
output_file = "請求書_テスト.pdf"
c = canvas.Canvas(output_file, pagesize=A4)

# 日本語フォントに切り替え
c.setFont("Meiryo", 24)

# テキスト描画
# (x座標, y座標, テキスト) で位置を指定
# 単位はポイント、左下が原点(0,0)
c.drawString(200, 750, "請求書")

c.setFont("Meiryo", 12)
c.drawString(100, 700, "株式会社サンプル 御中")
c.drawString(100, 680, "発行日:2026年6月8日")
c.drawString(100, 660, "請求番号:INV-202606-001")

# PDFを保存
c.save()

print(f"PDFを作成しました:{output_file}")

コードの解説

  • **pagesize=A4**:A4サイズで作成(landscapeにすれば横向きも可)
  • **registerFont**:日本語フォントをreportlabに登録
  • **c.setFont**:使うフォントとサイズを指定
  • **drawString(x, y, text)**:座標を指定して文字を描画。左下が(0,0)、A4の右上は(595, 842)
  • **c.save()**:最後に必ず呼ぶ。これがないとPDFが保存されない

第4章|表・罫線付きのプロフェッショナルなPDF

本格的な請求書PDFのコード

実務で使えるレベルの請求書PDFを作ります。タイトル・宛先・明細表・合計・振込先など、必要な要素をすべて含めます。

from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib import colors

# === 日本語フォント設定 ===
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc"
pdfmetrics.registerFont(TTFont("Meiryo", FONT_PATH))

def create_invoice(
    output_file,
    company_name,    # 宛先社名
    invoice_no,      # 請求番号
    issue_date,      # 発行日
    items            # 明細リスト [(品名, 数量, 単価), ...]
):
    """
    1枚の請求書PDFを作成する関数
    """
    c = canvas.Canvas(output_file, pagesize=A4)
    width, height = A4  # 595 x 842
    
    # === タイトル ===
    c.setFont("Meiryo", 24)
    c.drawCentredString(width / 2, height - 80, "請求書")
    
    # 上部の横線(タイトル装飾)
    c.setLineWidth(1)
    c.line(50, height - 100, width - 50, height - 100)
    
    # === 宛先 ===
    c.setFont("Meiryo", 14)
    c.drawString(60, height - 140, f"{company_name} 御中")
    
    # 宛先の下の横線
    c.line(60, height - 145, 350, height - 145)
    
    # === 発行情報(右上) ===
    c.setFont("Meiryo", 10)
    c.drawString(width - 200, height - 140, f"請求番号:{invoice_no}")
    c.drawString(width - 200, height - 155, f"発行日:{issue_date}")
    
    # === 自社情報 ===
    c.setFont("Meiryo", 12)
    c.drawString(width - 200, height - 200, "マイペース株式会社")
    c.setFont("Meiryo", 10)
    c.drawString(width - 200, height - 215, "〒100-0001 東京都千代田区...")
    c.drawString(width - 200, height - 230, "TEL:03-1234-5678")
    
    # === 合計金額 ===
    total = sum(qty * unit_price for _, qty, unit_price in items)
    c.setFont("Meiryo", 16)
    c.drawString(60, height - 250, f"ご請求金額:¥{total:,}(税込)")
    
    # === 明細テーブル ===
    table_top = height - 320
    row_height = 30
    col_widths = [240, 70, 100, 120]  # 品名, 数量, 単価, 金額
    col_positions = [60, 60+240, 60+310, 60+410]
    
    # ヘッダー行(薄い背景色)
    c.setFillColor(colors.HexColor("#E8F1FF"))
    c.rect(60, table_top, sum(col_widths), row_height, fill=True, stroke=False)
    
    c.setFillColor(colors.black)
    c.setFont("Meiryo", 11)
    headers = ["品名", "数量", "単価", "金額"]
    for i, header in enumerate(headers):
        c.drawString(col_positions[i] + 10, table_top + 10, header)
    
    # データ行
    c.setFont("Meiryo", 10)
    y = table_top - row_height
    for item_name, qty, unit_price in items:
        amount = qty * unit_price
        c.drawString(col_positions[0] + 10, y + 10, item_name)
        c.drawRightString(col_positions[1] + 60, y + 10, str(qty))
        c.drawRightString(col_positions[2] + 90, y + 10, f"¥{unit_price:,}")
        c.drawRightString(col_positions[3] + 110, y + 10, f"¥{amount:,}")
        y -= row_height
    
    # テーブル罫線
    table_bottom = y + row_height
    table_height = table_top - table_bottom + row_height
    c.setLineWidth(0.5)
    c.rect(60, table_bottom, sum(col_widths), table_height, stroke=True, fill=False)
    
    # === 振込先情報 ===
    c.setFont("Meiryo", 11)
    c.drawString(60, 200, "【お振込先】")
    c.setFont("Meiryo", 10)
    c.drawString(60, 180, "○○銀行 ○○支店")
    c.drawString(60, 165, "普通預金 1234567")
    c.drawString(60, 150, "口座名義:マイペース カブシキガイシャ")
    
    # === フッター ===
    c.setFont("Meiryo", 8)
    c.drawCentredString(width / 2, 50, "本請求書は自動生成されています。")
    
    c.save()
    print(f"請求書を作成しました:{output_file}")


# === 実行 ===
items = [
    ("コンサルティング料", 1, 100000),
    ("システム保守費", 1, 50000),
    ("追加開発", 5, 8000),
]

create_invoice(
    output_file="請求書_株式会社サンプル_202606.pdf",
    company_name="株式会社サンプル",
    invoice_no="INV-202606-001",
    issue_date="2026年6月8日",
    items=items
)

このコードでできること

  • タイトル「請求書」の中央配置
  • 宛先・発行情報の左右配置
  • 合計金額の自動計算
  • 明細テーブルの罫線・ヘッダー背景色
  • 振込先情報の表示

第5章|CSVから一括で請求書を生成

ここが**Python自動化の真骨頂**。CSVに「顧客リスト」を用意するだけで、何百枚もの請求書PDFが一気に出来上がります。

CSVファイルの準備

以下のような構造のCSVを用意します(顧客リスト.csv):

顧客名,請求番号,発行日,品名,数量,単価
株式会社A,INV-202606-001,2026-06-08,コンサルティング,1,100000
株式会社A,INV-202606-001,2026-06-08,保守費,1,50000
株式会社B,INV-202606-002,2026-06-08,コンサルティング,1,80000
株式会社C,INV-202606-003,2026-06-08,システム開発,10,15000
株式会社C,INV-202606-003,2026-06-08,保守費,1,30000

一括生成スクリプト

import pandas as pd
import os
# 前の章で定義した create_invoice 関数を使用

# === CSV読み込み ===
df = pd.read_csv("顧客リスト.csv")

# === 出力フォルダ作成 ===
output_folder = "請求書_2026年6月分"
os.makedirs(output_folder, exist_ok=True)

# === 顧客ごとにグループ化して請求書を生成 ===
# groupbyで顧客+請求番号でまとめる
for (customer, invoice_no, issue_date), group in df.groupby([
    "顧客名", "請求番号", "発行日"
]):
    # 明細リストを作成(品名・数量・単価のタプルのリスト)
    items = list(zip(group["品名"], group["数量"], group["単価"]))
    
    # ファイル名を組み立て
    output_file = f"{output_folder}/請求書_{customer}_{issue_date}.pdf"
    
    # 請求書PDFを生成
    create_invoice(
        output_file=output_file,
        company_name=customer,
        invoice_no=invoice_no,
        issue_date=issue_date,
        items=items
    )

print(f"\n{df['顧客名'].nunique()}社の請求書を作成しました")

このスクリプトのポイント

  • **groupby**で顧客ごとにデータをまとめる
  • **list(zip(…))**で品名・数量・単価をタプルのリストに変換
  • **os.makedirs**で出力フォルダを自動作成
  • ファイル名に顧客名と発行日を入れて、一覧で見やすく

第6章|Excelテンプレを使う方法

「会社で決まったExcelテンプレートがある」「PDFのデザインをreportlabで再現するのが大変」――そんな場合は、**Excelテンプレを使う方法**が便利です。

方法1|openpyxlでExcelを編集→PDF変換

既存のExcel請求書テンプレートに値を埋め込み、Excelに用意されているPDF変換機能を使う方法。

import openpyxl
from openpyxl import load_workbook
import shutil

# テンプレートをコピーして編集
shutil.copy("請求書テンプレ.xlsx", "請求書_出力.xlsx")
wb = load_workbook("請求書_出力.xlsx")
ws = wb.active

# 値を埋め込む(テンプレのセル位置に合わせて)
ws["B5"] = "株式会社サンプル"  # 宛先
ws["F3"] = "INV-202606-001"   # 請求番号
ws["F4"] = "2026年6月8日"      # 発行日
ws["C15"] = "コンサルティング"  # 品目1
ws["E15"] = 1                  # 数量
ws["F15"] = 100000             # 単価

# 保存
wb.save("請求書_出力.xlsx")

※ExcelからPDFへの変換は、Windowsの場合「win32com」ライブラリでExcel自体を操作する方法、Macの場合「LibreOffice CLI」で変換する方法などがあります。環境依存が強いため、上記コードはExcel編集までを示しました。

方法2|HTMLテンプレ+weasyprintでPDF化

HTMLでデザインしたテンプレを、PDFに変換する方法。デザインの自由度が高く、おすすめです。

# weasyprintをインストール
# uv add weasyprint Jinja2

from jinja2 import Template
from weasyprint import HTML

# HTMLテンプレート
html_template = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: "Meiryo", sans-serif; }
h1 { text-align: center; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #999; padding: 8px; }
th { background: #E8F1FF; }
.total { font-size: 18px; font-weight: bold; color: #0066ff; }
</style>
</head>
<body>
<h1>請求書</h1>
<p>{{ company_name }} 御中</p>
<p>請求番号:{{ invoice_no }} 発行日:{{ issue_date }}</p>
<p class="total">ご請求金額:¥{{ total }}(税込)</p>
<table>
<tr><th>品名</th><th>数量</th><th>単価</th><th>金額</th></tr>
{% for name, qty, price in items %}
<tr>
<td>{{ name }}</td><td>{{ qty }}</td>
<td>¥{{ price }}</td><td>¥{{ qty * price }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
"""

# データを埋め込む
items = [("コンサルティング", 1, 100000), ("保守費", 1, 50000)]
total = sum(qty * price for _, qty, price in items)

template = Template(html_template)
html_content = template.render(
    company_name="株式会社サンプル",
    invoice_no="INV-202606-001",
    issue_date="2026年6月8日",
    items=items,
    total=f"{total:,}"
)

# HTMLからPDFを生成
HTML(string=html_content).write_pdf("請求書_出力.pdf")
print("PDFを生成しました")

第7章|実務スクリプト|月次請求書バッチ

ここまでの内容を統合した、実務で使える完成スクリプトです。月初に1回実行するだけで、全顧客の請求書PDFが生成されます。

完成スクリプト

"""
月次請求書バッチ
- 顧客リスト.csvを読み込む
- 顧客ごとに請求書PDFを生成
- 出力フォルダに保存
- 実行ログを出力
"""
import pandas as pd
import os
from datetime import datetime
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib import colors

# === 設定 ===
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc"
pdfmetrics.registerFont(TTFont("Meiryo", FONT_PATH))

CSV_PATH = "顧客リスト.csv"
CURRENT_MONTH = datetime.now().strftime("%Y年%m月")
OUTPUT_FOLDER = f"請求書_{datetime.now().strftime('%Y%m')}"

# === 請求書PDF作成関数 ===
# (第4章のcreate_invoice関数をここに展開)
# 簡潔のため省略...

# === メイン処理 ===
def main():
    print(f"=== 月次請求書バッチ開始:{CURRENT_MONTH} ===")
    
    # 出力フォルダ作成
    os.makedirs(OUTPUT_FOLDER, exist_ok=True)
    
    # CSV読み込み
    df = pd.read_csv(CSV_PATH)
    print(f"顧客リスト読み込み完了:{len(df)}行")
    
    # ログファイル準備
    log_file = f"{OUTPUT_FOLDER}/実行ログ.txt"
    
    success_count = 0
    error_count = 0
    
    # ログをファイルに書き出す
    with open(log_file, "w", encoding="utf-8") as f:
        f.write(f"実行日時:{datetime.now()}\n")
        f.write(f"対象月:{CURRENT_MONTH}\n")
        f.write("=" * 50 + "\n")
        
        # 顧客ごとにグループ化して請求書生成
        for (customer, invoice_no, issue_date), group in df.groupby([
            "顧客名", "請求番号", "発行日"
        ]):
            try:
                items = list(zip(group["品名"], group["数量"], group["単価"]))
                total = sum(qty * price for _, qty, price in items)
                
                output_file = f"{OUTPUT_FOLDER}/請求書_{customer}_{issue_date}.pdf"
                
                # PDF生成(create_invoice関数を呼ぶ)
                create_invoice(
                    output_file=output_file,
                    company_name=customer,
                    invoice_no=invoice_no,
                    issue_date=issue_date,
                    items=items
                )
                
                # ログ書き込み
                f.write(f"✓ {customer}:¥{total:,}\n")
                success_count += 1
                
            except Exception as e:
                f.write(f"✗ {customer}:エラー({e})\n")
                error_count += 1
        
        # サマリ
        f.write("=" * 50 + "\n")
        f.write(f"成功:{success_count}件\n")
        f.write(f"エラー:{error_count}件\n")
    
    print(f"\n=== 完了 ===")
    print(f"成功:{success_count}件、エラー:{error_count}件")
    print(f"出力フォルダ:{OUTPUT_FOLDER}/")


if __name__ == "__main__":
    main()

このスクリプトでできること

  • CSVから顧客リストを読み込み
  • 顧客ごとに個別の請求書PDFを自動生成
  • 発行月のフォルダに自動振り分け
  • 実行ログをテキストファイルに保存
  • エラーが発生しても他の処理を続行

毎月の定期実行は、Windowsのタスクスケジューラ・Macのcronで自動化できます。月初の朝9時に自動実行など、完全無人化も可能です。

第8章|つまずき対処&まとめ

よくあるトラブル

トラブル1:日本語が文字化け(□□□と表示される)

原因:

日本語フォントを登録していない、またはフォントパスが間違っている。

対処:

# フォントパスを確認・修正
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc"  # Windows
# FONT_PATH = "/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc"  # Mac

pdfmetrics.registerFont(TTFont("Meiryo", FONT_PATH))
c.setFont("Meiryo", 12)  # 必ずフォントを切り替える

トラブル2:座標がズレる

原因:

reportlabは「左下が(0,0)」で、Excelの感覚(左上が原点)と逆。

対処:

「高さからy座標を引く」発想で位置を決める。A4の高さは842ポイント。上から100ポイント下げたい場合はy=842-100=742。

トラブル3:PDFが開けない・破損する

原因:

c.save() を呼んでいない。

対処:

最後に必ず c.save() を呼ぶ。これがないとPDFファイルが完成しません。

トラブル4:CSVの読み込みエラー

原因:

CSVのエンコードがShift_JISだが、pandasがUTF-8で読もうとしている。

対処:

# エンコードを指定して読み込み
df = pd.read_csv("顧客リスト.csv", encoding="utf-8")
# または
df = pd.read_csv("顧客リスト.csv", encoding="shift_jis")

この記事のまとめ

  • Pythonで請求書PDFを自動生成すれば、月3時間の業務が10秒に
  • reportlabライブラリで、デザイン自由なPDFが作れる
  • 日本語フォント設定(メイリオ)が必須
  • CSVと組み合わせれば、複数顧客の一括生成が可能
  • HTMLテンプレ+weasyprintならデザインがさらに自由
  • 月次バッチ化で完全自動化も実現できる

FAQ

Q1. PDFのデザインを既存テンプレに合わせたい

Excelテンプレに値を埋め込んでExcel機能でPDF変換する方法、またはHTMLテンプレ+weasyprintで再現する方法があります。後者の方がデザインの自由度が高く、おすすめです。

Q2. 印鑑画像を埋め込みたい

reportlabのc.drawImage(image_path, x, y, width, height)で画像を埋め込めます。透過PNG形式の印鑑画像を準備しましょう。

Q3. メール送信まで自動化したい

本記事の請求書生成と、別記事「Pythonでメール自動送信」を組み合わせれば、PDF生成→メール送信まで完全自動化できます(ただしメール環境設定は複雑なため注意)。

Q4. 複数ページの請求書にしたい

明細が多くて1ページに収まらない場合は、c.showPage() を呼んで改ページします。次のページの内容を再度描画するロジックを書きましょう。

Q5. 顧客情報をExcelに保存したい

CSVではなくExcelで管理したい場合は、pandasのpd.read_excel(“顧客リスト.xlsx”)で読み込めます。複数シートの管理にも対応できます。

次にやるべき3つの行動

  1. **今すぐ**:自分の業務で「毎月作る定型書類」をリストアップ(請求書・領収書・報告書など)
  2. **今日中**:第3章の最小コードを実行し、最初の1枚を作ってみる
  3. **今週中**:自社のテンプレに合わせてカスタマイズし、CSV一括生成を試す

月3時間の業務が10秒に。一度仕組みを作れば、毎月の請求書業務は「ボタン1つ」になります。

PDF自動化で、業務が変わる

請求書だけでなく、領収書・契約書・レポート・案内文――定型のPDF業務は、Pythonですべて自動化できます。

「毎月の作業」を「ボタン1つ」に変えれば、空いた時間で本来の業務に集中できます。それが、業務効率化の本質です。

最新の解説記事は、新着記事から順次公開しています。X(旧Twitter)でも更新情報を発信していますので、ぜひフォローしてください。

この記事を書いた人

ソニック|バックオフィス出身の業務効率化ブロガー。事務職時代に毎月100社分の請求書作成に3時間かかっていた経験を、Pythonで自動化した実体験あり。現在のデータサイエンス業務でもレポート自動生成にPythonを活用中。リアルな実体験をもとにしたノウハウを発信中。

→ 詳しいプロフィールはこちら→ はじめての方へ

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

コメント

コメントする

CAPTCHA


目次