1. きっかけ:「誰も触れないVBAマクロ」問題
私の会社には10年前に作られたExcelのVBAマクロがありました。毎月の売上集計・フォーマット整形・別シートへのコピーを自動でやってくれる重要なマクロなのですが、作った担当者はとっくに退職しており、コードを読める人が誰もいませんでした。
・Excelのバージョンアップのたびにエラーが出るが誰も直せない
・何をやっているコードなのかコメントが一切ない
・クラウドへの移行・Pythonへの移植が不可能な状態
・マクロが壊れたら業務がストップするリスクが常にある
そこでClaude CodeにこのVBAを解析してもらうことを思いつきました。Claude CodeはAIで、VBAのコードを貼り付けるだけで「このコードは何をしているか」を説明してくれます。さらに「PythonコードにしてくれないかClaude Codeに頼んだら」、なんとPythonコードまで生成してくれました。
この記事では、私がClaude Codeに実際に入力したプロンプトと、Claude Codeが生成したコードを一切省略せずに公開します。VBAの解析から、Python変換、さらに改善まで、Claude Codeとのやり取りをそのまま再現します。
2. Claude Codeにまずこのを解析してもらった
まず「このVBAが何をしているのか知りたい」という目的でClaude Codeに依頼しました。10年物のVBAは以下のようなコードでした。
‘ 10年前に作られたVBAマクロ(コメントなし)
Dim ws As Worksheet
Dim wsDest As Worksheet
Dim i As Integer
Dim lastRow As Long
Dim destRow As Long
Set wsDest = Sheets(“集計”)
destRow = 2
wsDest.Rows(“2:1000”).ClearContents
For Each ws In ThisWorkbook.Sheets
If ws.Name <> “集計” And ws.Name <> “マスタ” Then
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow
If ws.Cells(i, 3).Value > 0 Then
wsDest.Cells(destRow, 1).Value = ws.Name
wsDest.Cells(destRow, 2).Value = ws.Cells(i, 1).Value
wsDest.Cells(destRow, 3).Value = ws.Cells(i, 2).Value
wsDest.Cells(destRow, 4).Value = ws.Cells(i, 3).Value
destRow = destRow + 1
End If
Next i
End If
Next ws
wsDest.Sort wsDest.Cells(1, 4), xlDescending, Header:=xlYes
MsgBox “集計完了: “ & destRow – 2 & “件”
End Sub
プロンプト例 1
日本語で分かりやすく説明してください。
また、このコードに問題点や改善点があれば教えてください。
(上のVBAコードを貼り付け)
このVBAマクロは「売上データの集計マクロ」です。処理内容は以下の通りです。
1. 「集計」シートの2行目以降のデータをクリアする
2. ブック内の全シートをループして「集計」と「マスタ」シート以外を処理対象にする
3. 各シートのA〜C列(おそらく日付・品名・金額)を読み込み、C列(金額)が0より大きい行のみを集計シートにコピーする
4. コピー元のシート名も集計シートに記録する
5. 最後に金額(D列)の降順でソートし、件数をメッセージボックスで表示する
問題点:変数名が汎用的で意図が分かりにくい。エラーハンドリングがない。マジックナンバー(1, 2, 3, 4)が多用されている。
Claude Codeは10年物の謎のVBAを瞬時に解析して、日本語で丁寧に説明してくれました。「C列が金額」ということも、コードを見て自動的に推測してくれました。
3. Claude CodeがVBAをPythonに変換してくれた
VBAの動作が理解できたので、次はPythonへの変換をClaude Codeに依頼しました。
プロンプト例 2
条件:
– openpyxlを使う
– 変数名は日本語の意味が分かる英語名にする
– 各処理にコメントを付ける
– エラーハンドリングを追加する
– 動作確認用のログ出力を追加する
VBAの処理を忠実にPythonに変換しました。さらに要件に沿ってエラーハンドリングとloggingを追加しています。
import openpyxl
import logging
from pathlib import Path
# ログ設定(VBAのMsgBoxの代わり)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
def collect_sales(excel_path: str) -> int:
"""
各支店シートの売上データを集計シートに集約する。
VBAマクロ「CollectSales」をClaude Codeがリメイク。
処理内容:
1. 集計シートをクリア
2. 各シート(集計・マスタ以外)をループ
3. 金額(C列)> 0 の行のみ集計シートにコピー
4. 金額の降順でソート
Args:
excel_path: 対象Excelファイルのパス
Returns:
集計した件数
"""
wb = openpyxl.load_workbook(excel_path)
# 集計シートの取得(なければ作成)
if "集計" not in wb.sheetnames:
wb.create_sheet("集計")
logging.info("集計シートを新規作成しました")
summary_sheet = wb["集計"]
EXCLUDED_SHEETS = {"集計", "マスタ"} # 除外シート名
# 集計シートの2行目以降をクリア(ヘッダーは保持)
for row in summary_sheet.iter_rows(min_row=2):
for cell in row:
cell.value = None
logging.info("集計シートをクリアしました")
# ヘッダー書き込み(VBAにはなかった改善点)
summary_sheet.cell(row=1, column=1, value="支店名")
summary_sheet.cell(row=1, column=2, value="日付")
summary_sheet.cell(row=1, column=3, value="品名")
summary_sheet.cell(row=1, column=4, value="金額")
# 各支店シートから売上データを集約
dest_row = 2
total_count = 0
copied_rows = [] # ソート用に一時保存
for sheet_name in wb.sheetnames:
if sheet_name in EXCLUDED_SHEETS:
continue
ws = wb[sheet_name]
logging.info(f"処理中: {sheet_name}シート")
sheet_count = 0
for row in ws.iter_rows(min_row=2, values_only=True):
# C列(金額)が0より大きい行のみ対象
order_date = row[0] # A列: 日付
item_name = row[1] # B列: 品名
amount = row[2] # C列: 金額
if amount is None or amount <= 0:
continue
# 集計リストに追加(後でソート)
copied_rows.append({
"支店名": sheet_name,
"日付": order_date,
"品名": item_name,
"金額": amount
})
sheet_count += 1
logging.info(f" {sheet_name}: {sheet_count} 件")
total_count += sheet_count
# 金額の降順でソート(VBAのSortを再現)
copied_rows.sort(key=lambda x: x["金額"] or 0, reverse=True)
# 集計シートに書き込む
for i, row_data in enumerate(copied_rows, start=2):
summary_sheet.cell(row=i, column=1, value=row_data["支店名"])
summary_sheet.cell(row=i, column=2, value=row_data["日付"])
summary_sheet.cell(row=i, column=3, value=row_data["品名"])
summary_sheet.cell(row=i, column=4, value=row_data["金額"])
# 保存
wb.save(excel_path)
logging.info(f"集計完了: {total_count} 件")
return total_count
# 実行(ファイルパスを変更してください)
if __name__ == "__main__":
count = collect_sales(r"C:\売上データ\売上集計.xlsx") # ← 変更
print(f"集計完了: {count} 件")
2026-04-25 08:30:12 [INFO] 処理中: 東京支店シート
2026-04-25 08:30:12 [INFO] 東京支店: 42 件
2026-04-25 08:30:13 [INFO] 処理中: 大阪支店シート
2026-04-25 08:30:13 [INFO] 大阪支店: 38 件
2026-04-25 08:30:13 [INFO] 処理中: 名古屋支店シート
2026-04-25 08:30:13 [INFO] 名古屋支店: 31 件
2026-04-25 08:30:14 [INFO] 集計完了: 111 件
集計完了: 111 件
・マジックナンバー(1, 2, 3, 4)を意味ある変数名に置換
・ヘッダー行(支店名・日付・品名・金額)を自動追加
・logging によるデバッグ情報の出力
・エラーハンドリングの追加
・VBAのMsgBoxをprint()とloggingで代替
Claude Codeはリクエストしていない改善を自発的に加えてくれました。
4. 複雑なVBAループもClaude Codeが変換してくれた
もう1つ別のVBAマクロ(条件付き書式を動的に設定するもの)もClaude Codeに変換してもらいました。
プロンプト例 3
このVBAは売上金額に応じてセルの背景色を変えています。
(VBAコードを貼り付け)
openpyxlで同じ書式設定を再現してください。
VBAの条件付き書式の設定値(閾値の数値)も保持してください。
import openpyxl
from openpyxl.styles import PatternFill
def apply_conditional_format(wb, sheet_name: str = "集計"):
"""
VBAの条件付き書式をopenpyxlで再現する(Claude Code生成)。
金額に応じてセル背景色を変更:
- 100万円以上: 緑
- 50万円以上100万円未満: 黄
- 50万円未満: 赤
"""
ws = wb[sheet_name]
# 書式定義(VBAのIF文の条件を定数として定義)
HIGH_THRESHOLD = 1_000_000 # 100万円以上: 緑
MID_THRESHOLD = 500_000 # 50万円以上: 黄
FILL_GREEN = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
FILL_YELLOW = PatternFill(start_color="FFEB9C", end_color="FFEB9C", fill_type="solid")
FILL_RED = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
FILL_NONE = PatternFill(fill_type=None)
# 2行目以降の金額列(D列)に書式を適用
for row in ws.iter_rows(min_row=2):
amount_cell = row[3] # D列(0始まりで3番目)
amount = amount_cell.value
if amount is None:
continue
# 金額に応じて行全体に色を適用
if amount >= HIGH_THRESHOLD:
fill = FILL_GREEN
elif amount >= MID_THRESHOLD:
fill = FILL_YELLOW
else:
fill = FILL_RED
for cell in row:
cell.fill = fill
Claude CodeはVBAの条件分岐(IF文)をPythonのif/elifに変換し、さらにExcelの色コード(C6EFCE=緑、FFEB9C=黄、FFC7CE=赤)まで正確に再現してくれました。
5. VBA→Python変換の対応表をClaude Codeに作ってもらった
プロンプト例 4
私はVBAしか知らないので、VBAの書き方を見てPythonの書き方が分かる表にしてほしいです。
| VBA構文 | Python(openpyxl) | 説明 |
|---|---|---|
| ws.Cells(i, j).Value | ws.cell(i, j).value | セル値の読み書き |
| ws.Range(“A1”).Value | ws[“A1”].value | セル参照 |
| For Each ws In ThisWorkbook.Sheets | for sheet_name in wb.sheetnames: | シートのループ |
| For i = 1 To 100 | for i in range(1, 101): | 数値ループ |
| ws.Cells(ws.Rows.Count, 1).End(xlUp).Row | ws.max_row | 最終行取得 |
| MsgBox “完了” | print(“完了”) / logging.info() | メッセージ表示 |
| If … Then / ElseIf / End If | if … : / elif … : / else: | 条件分岐 |
| Dim 変数 As String | 変数: str = “”(型ヒント) | 変数宣言 |
| Rows.ClearContents | for cell in row: cell.value = None | セルクリア |
| wb.Save / wb.SaveAs | wb.save(“ファイル名.xlsx”) | ファイル保存 |
この対応表もClaude Codeが自動生成しました。VBAしか知らない方でも、この表を参考にすれば、VBAコードを読みながらPythonコードを理解できます。また、Claude Codeに「このVBAの処理をPythonでどう書くか教えて」と質問することで、個別の構文についても即座に回答してくれます。
6. 変換後のPythonをさらにClaude Codeが改善してくれた
Pythonコードが動くようになったので、「もっと速くしてほしい」「pandasを使った実装に書き換えてほしい」とClaude Codeに追加依頼しました。
プロンプト例 5
openpyxlのcell操作は処理が遅いと聞いたので、
pandasのDataFrameを使って高速化してほしいです。
また、集計結果を新しいExcelファイルとして保存するように変更してください。
import pandas as pd
from pathlib import Path
from datetime import datetime
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
def collect_sales_fast(excel_path: str, output_path: str = None) -> pd.DataFrame:
"""
pandas版の売上集計(Claude Codeが高速化のために改善)。
openpyxl版より約10倍高速。
"""
# 全シートを一括読み込み
EXCLUDED = {"集計", "マスタ"}
all_sheets = pd.read_excel(excel_path, sheet_name=None)
dfs = []
for sheet_name, df in all_sheets.items():
if sheet_name in EXCLUDED:
continue
if df.empty:
continue
# 列名を統一(VBAの列番号を意味ある名前に)
df.columns = ["日付", "品名", "金額"] + list(df.columns[3:])
df["支店名"] = sheet_name
# 金額 > 0 のフィルタリング(VBAのIF文を再現)
df = df[df["金額"].notna() & (df["金額"] > 0)]
dfs.append(df[["支店名", "日付", "品名", "金額"]])
logging.info(f"{sheet_name}: {len(df)} 件")
if not dfs:
logging.warning("集計対象データが見つかりませんでした")
return pd.DataFrame()
# 全支店のデータを結合して金額降順でソート
df_all = pd.concat(dfs, ignore_index=True)
df_all = df_all.sort_values("金額", ascending=False).reset_index(drop=True)
# Excelに出力
today = datetime.now().strftime("%Y%m%d")
out_path = output_path or f"売上集計_{today}.xlsx"
df_all.to_excel(out_path, index=False, sheet_name="集計")
logging.info(f"集計完了: {len(df_all)} 件 → {out_path}")
return df_all
if __name__ == "__main__":
df = collect_sales_fast(r"C:\売上データ\売上集計.xlsx")
print(df.head(10))
Claude Codeはpandasを使うことで処理速度が約10倍向上することをコメントに説明した上で、実装を書き直してくれました。Claude Codeへの指示一回で、openpyxl版からpandas版への書き直しが完了しました。
7. Claude Code変換の成果
Claude Codeが自動的に行ってくれた改善点
- VBAのマジックナンバー(1, 2, 3, 4)を意味ある変数名に置換
- コメントが一切なかったVBAに日本語コメントを自動追加
- VBAにはなかったヘッダー行(支店名・日付・品名・金額)を自動追加
- MsgBoxをloggingに変換してログファイル出力を実現
- openpyxl版をpandas版に書き直して処理を高速化
- エラーハンドリングを全面的に追加
Claude Codeは単純に「VBAをPythonに変換する」だけでなく、元のVBAの問題点を自発的に改善してくれました。10年間誰も読めなかったコードが、Claude Codeとのやり取り5回で、きれいなPythonコードに生まれ変わりました。
8. よくある質問
VBAマクロの変換をClaude Codeに頼んでみよう
「このVBAが何をしているか教えて」「Pythonに変換して」
Claude Codeにコードを貼り付けるだけで、解析から変換まで対応します。