フリーランスエンジニアが提案書・見積書をPythonで自動生成する方法【openpyxl×テンプレート活用】
「毎回Excelを開いて、コピペして、金額を直して…この作業、地味に時間かかるんだよな」
フリーランスとして案件をこなしていると、こんな悩みが出てきますよね。
提案書・見積書の作成って、1件あたり15〜30分はかかります。月に5〜10件こなしていたら、それだけで数時間が消えていく計算です。しかもミス(単価の入力間違い・合計金額のズレ)が起きやすい。
そこで今回は、Pythonで提案書・見積書を自動生成するスクリプトの作り方を解説します。使うのは openpyxl と python-docx の2本柱。テンプレートさえ作ればあとはコマンド1発で完成です。
📌 対象読者:Python基礎がわかる初〜中級者 / フリーランスエンジニア・副業エンジニアの方
なぜPythonで自動化するのか?

手作業の見積書作成には、こんな問題があります。
- ✅ コピペミス・入力ミスが起きやすい
- ✅ 案件ごとにフォーマットがバラバラになりがち
- ✅ 修正が入るたびに全部やり直し
- ✅ 単純作業に時間をとられて、本業に集中できない
Pythonで自動化すると、データ(品目・単価・数量)を辞書やCSVで管理するだけで、見積書が一発で生成できます。フォーマットは常に統一されるし、修正もデータを直すだけでOK。
つまり「書類を作る時間」を「案件を取る時間」に変換できるわけです。
今回作るものの全体像
今回は以下の構成でスクリプトを作ります。
- 📄 見積書(Excel形式):openpyxlで生成。品目・単価・数量・小計・消費税・合計を自動計算
- 📝 提案書(Word形式):python-docxで生成。クライアント名・提案内容・納期をテンプレートから差し込み
まずはライブラリをインストールしておきましょう。
pip install openpyxl python-docx
STEP 1:見積書をExcelで自動生成する
最初に、一番使用頻度が高いExcel形式の見積書から作ります。
ポイントをまとめるとこんな感じです。
- 案件情報と品目リストを辞書で管理
- openpyxlでシートにデータを書き込む
- 合計金額・消費税はPython側で計算して入力
# estimate_generator.py
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from datetime import date
def generate_estimate(client_info: dict, items: list, output_path: str):
"""
見積書をExcelで自動生成する関数
client_info: クライアント情報の辞書
items: 品目リスト(品目名・単価・数量を含む辞書のリスト)
output_path: 出力先ファイルパス
"""
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "見積書"
# --- カラム幅の設定 ---
ws.column_dimensions['A'].width = 5
ws.column_dimensions['B'].width = 30
ws.column_dimensions['C'].width = 15
ws.column_dimensions['D'].width = 10
ws.column_dimensions['E'].width = 18
# --- スタイル定義 ---
header_font = Font(bold=True, size=11)
title_font = Font(bold=True, size=16)
fill_blue = PatternFill(fill_type="solid", fgColor="4472C4")
fill_light = PatternFill(fill_type="solid", fgColor="DCE6F1")
thin_border = Border(
left=Side(style='thin'), right=Side(style='thin'),
top=Side(style='thin'), bottom=Side(style='thin')
)
# --- タイトル ---
ws['B1'] = "御見積書"
ws['B1'].font = title_font
ws['B1'].alignment = Alignment(horizontal='center')
ws.merge_cells('B1:E1')
# --- 発行日・見積番号 ---
ws['D3'] = "発行日:"
ws['E3'] = date.today().strftime("%Y年%m月%d日")
ws['D4'] = "見積番号:"
ws['E4'] = client_info.get('estimate_no', 'EST-001')
# --- クライアント情報 ---
ws['B3'] = f"宛先:{client_info['client_name']} 御中"
ws['B3'].font = Font(bold=True, size=12)
ws['B4'] = f"件名:{client_info['project_name']}"
ws['B5'] = f"有効期限:見積発行より{client_info.get('valid_days', 30)}日間"
# --- テーブルヘッダー ---
header_row = 8
headers = ['No.', '品目・作業内容', '単価(円)', '数量', '小計(円)']
header_cols = ['A', 'B', 'C', 'D', 'E']
for col, header in zip(header_cols, headers):
cell = ws[f"{col}{header_row}"]
cell.value = header
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = fill_blue
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# --- 品目の書き込み ---
subtotal = 0
for i, item in enumerate(items):
row = header_row + 1 + i
unit_price = item['unit_price']
quantity = item['quantity']
item_total = unit_price * quantity
subtotal += item_total
row_data = [i + 1, item['name'], unit_price, quantity, item_total]
for col, value in zip(header_cols, row_data):
cell = ws[f"{col}{row}"]
cell.value = value
cell.border = thin_border
cell.alignment = Alignment(horizontal='right' if col in ['A', 'C', 'D', 'E'] else 'left')
# 数値はカンマ区切りで表示
if col in ['C', 'E']:
cell.number_format = '#,##0'
# --- 合計・税額 ---
total_row = header_row + len(items) + 2
tax_rate = 0.10
tax = int(subtotal * tax_rate)
total = subtotal + tax
summary = [
("小計", subtotal),
("消費税(10%)", tax),
("合計金額(税込)", total),
]
for j, (label, value) in enumerate(summary):
row = total_row + j
ws[f"D{row}"] = label
ws[f"D{row}"].font = Font(bold=True)
ws[f"D{row}"].border = thin_border
ws[f"E{row}"] = value
ws[f"E{row}"].number_format = '#,##0'
ws[f"E{row}"].border = thin_border
ws[f"E{row}"].alignment = Alignment(horizontal='right')
# 合計行は背景色をつけて目立たせる
if label == "合計金額(税込)":
ws[f"D{row}"].fill = fill_light
ws[f"E{row}"].fill = fill_light
ws[f"E{row}"].font = Font(bold=True, size=12)
wb.save(output_path)
print(f"✅ 見積書を生成しました: {output_path}")
# --- 実行サンプル ---
if __name__ == "__main__":
client_info = {
"client_name": "株式会社サンプル",
"project_name": "業務管理システム開発",
"estimate_no": "EST-2025-001",
"valid_days": 30,
}
items = [
{"name": "要件定義・設計", "unit_price": 150000, "quantity": 1},
{"name": "バックエンド開発(FastAPI)", "unit_price": 80000, "quantity": 3},
{"name": "フロントエンド開発", "unit_price": 70000, "quantity": 2},
{"name": "テスト・デバッグ", "unit_price": 50000, "quantity": 1},
{"name": "ドキュメント作成", "unit_price": 30000, "quantity": 1},
]
generate_estimate(client_info, items, "見積書_株式会社サンプル.xlsx")
STEP 2:提案書をWordで自動生成する
次はWord形式の提案書です。こちらは python-docx を使います。
ポイントはここです。
- クライアント名・プロジェクト名・提案内容・納期を引数で受け取る
- 見出し・本文・箇条書きを構造的に組み立てる
- 毎回ゼロから書くのではなく、雛形に差し込むイメージで使う
# proposal_generator.py
from docx import Document
from docx.shared import Pt, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from datetime import date
def generate_proposal(client_info: dict, proposal_details: dict, output_path: str):
"""
提案書をWordで自動生成する関数
client_info: クライアント情報
proposal_details: 提案内容(課題・解決策・スケジュール・費用感など)
output_path: 出力先ファイルパス
"""
doc = Document()
# --- ページ余白の設定 ---
section = doc.sections[0]
section.top_margin = Cm(2.5)
section.bottom_margin = Cm(2.5)
section.left_margin = Cm(2.5)
section.right_margin = Cm(2.5)
# --- タイトル ---
title = doc.add_heading('提案書', level=0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
# --- 基本情報ブロック ---
doc.add_paragraph("")
info_para = doc.add_paragraph()
info_para.add_run(f"宛先:{client_info['client_name']} 御中\n").bold = True
info_para.add_run(f"件名:{proposal_details['title']}\n")
info_para.add_run(f"作成日:{date.today().strftime('%Y年%m月%d日')}\n")
info_para.add_run(f"作成者:{client_info.get('author_name', '氏名未設定')}")
doc.add_paragraph("")
# --- 各セクションを動的に生成 ---
sections_data = [
("1. 背景・課題", proposal_details.get('background', [])),
("2. 提案内容", proposal_details.get('solution', [])),
("3. 期待される効果", proposal_details.get('benefits', [])),
("4. 実施スケジュール", proposal_details.get('schedule', [])),
("5. 費用概算", proposal_details.get('cost_summary', [])),
]
for section_title, bullet_items in sections_data:
doc.add_heading(section_title, level=2)
if isinstance(bullet_items, list):
for item in bullet_items:
doc.add_paragraph(item, style='List Bullet')
else:
doc.add_paragraph(str(bullet_items))
doc.add_paragraph("")
# --- フッター的な一言 ---
doc.add_paragraph("")
closing = doc.add_paragraph(
"ご不明な点がございましたら、いつでもご連絡ください。"
"引き続きよろしくお願いいたします。"
)
closing.alignment = WD_ALIGN_PARAGRAPH.LEFT
doc.save(output_path)
print(f"✅ 提案書を生成しました: {output_path}")
# --- 実行サンプル ---
if __name__ == "__main__":
client_info = {
"client_name": "株式会社サンプル",
"author_name": "山田 太郎",
}
proposal_details = {
"title": "業務管理システム導入のご提案",
"background": [
"現状、案件管理がExcelで行われており、更新の二重入力や集計ミスが発生している",
"リモートワーク対応のため、リアルタイムな情報共有の仕組みが求められている",
],
"solution": [
"FastAPIベースのWebアプリケーションとして新規開発",
"クラウドDBを活用し、どこからでもアクセス可能な環境を構築",
"既存Excelデータのインポート機能を実装し、移行コストを最小化",
],
"benefits": [
"入力作業の工数を月20時間以上削減(試算)",
"リアルタイム共有によるコミュニケーションコストの削減",
"データの一元管理によるミスの抑制",
],
"schedule": [
"1〜2週目:要件定義・設計",
"3〜6週目:開発・実装",
"7週目:テスト・修正",
"8週目:納品・運用開始サポート",
],
"cost_summary": [
"総費用(税込):約660,000円",
"詳細は別紙見積書をご参照ください",
],
}
generate_proposal(client_info, proposal_details, "提案書_業務管理システム.docx")
STEP 3:2つをまとめて一括生成するメインスクリプト
最後に、見積書と提案書を1回の実行でまとめて出力するスクリプトを作りましょう。実務ではこれが一番使いやすい形です。
# main.py(一括実行スクリプト)
from estimate_generator import generate_estimate
from proposal_generator import generate_proposal
import os
# --- 案件データをここに入力するだけ! ---
PROJECT = {
"client_name": "株式会社サンプル",
"project_name": "業務管理システム開発",
"estimate_no": "EST-2025-001",
"valid_days": 30,
"author_name": "山田 太郎",
}
ITEMS = [
{"name": "要件定義・設計", "unit_price": 150000, "quantity": 1},
{"name": "バックエンド開発(FastAPI)", "unit_price": 80000, "quantity": 3},
{"name": "フロントエンド開発", "unit_price": 70000, "quantity": 2},
{"name": "テスト・デバッグ", "unit_price": 50000, "quantity": 1},
{"name": "ドキュメント作成", "unit_price": 30000, "quantity": 1},
]
# 出力先フォルダを作成
os.makedirs("output", exist_ok=True)
client = PROJECT['client_name'].replace(' ', '_')
# 見積書・提案書を一括生成
generate_estimate(PROJECT, ITEMS, f"output/見積書_{client}.xlsx")
generate_proposal(
PROJECT,
{
"title": f"{PROJECT['project_name']}のご提案",
"background": ["現状の課題をここに記入"],
"solution": ["提案内容をここに記入"],
"benefits": ["期待効果をここに記入"],
"schedule": ["スケジュールをここに記入"],
"cost_summary": ["費用概算をここに記入(別紙見積書参照)"],
},
f"output/提案書_{client}.docx"
)
print("\n🎉 書類の生成が完了しました! output/ フォルダを確認してください。")
あとは PROJECT と ITEMS を書き換えて実行するだけ。案件情報の管理はシンプルな辞書なので、慣れてきたらCSVやJSONから読み込む形に拡張するのもおすすめです。
応用アイデア:もっと便利にするために
基本スクリプトができたら、こんな拡張を試してみてください。
- 📊 案件一覧をCSVで管理して、一括でまとめて生成する
- 📧 smtplibと組み合わせてメールに自動添付して送信
- 🗂 クライアントごとにフォルダを自動作成して整理する
- 🖨 win32comでExcelをPDF変換(Windowsのみ)してクライアントに送りやすくする
どれも既存のスクリプトに数十行追加するだけで実現できます。少しずつ自分の業務フローに合わせてカスタマイズしていくのが一番です。
まとめ
今回は openpyxlとpython-docxを使って、提案書・見積書を自動生成するPythonスクリプトの作り方を解説しました。
- ✅
openpyxlでExcel見積書を自動生成(スタイル・合計計算込み) - ✅
python-docxでWord提案書を構造化して生成 - ✅ メインスクリプトで1コマンド一括出力
書類作成に使っていた時間が浮けば、その分だけ本業の開発・営業・スキルアップに集中できます。フリーランスや副業エンジニアにとって、地味だけど効果絶大な自動化のひとつですよ。
ぜひ自分の案件データで試してみてください。「こういうフォーマットにしたい」「メール送信まで自動化したい」という場合も、ベースのコードがあれば応用はすぐできるはずです 💪
こちらも読まれています
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





