当サイトはアフィリエイトを含むプロモーションを掲載しています
PythonでExcelレポートを自動化する完全ガイド【2026年最新】初心者向けコード付き・VBAとの比較・AI活用・定期自動実行まで徹底解説
「毎週の売上レポート作成に2時間かかっている」「Excelでの集計でミスが絶えない」——Pythonを使えばこうした定型作業を自動化し、作業時間を80〜90%削減できます。
2026年のポイントは2つ。①Microsoft ExcelがPythonを直接サポートするようになり、環境構築なしでも試せるようになった。②ChatGPT・ClaudeなどのAIに「このコードを書いて」と頼むだけで動くコードが生成できるため、プログラミング未経験でも自動化に着手できる時代になった。この記事では、最新環境に対応した実践コード付きで完全解説します。
- VBAとPythonどちらを使うべきか——2026年の正直な比較
- 【2026年新機能】ExcelのPython機能とは何か——環境構築なしで試す方法
- Pythonでのレポート自動化に必要なライブラリとセットアップ(15分)
- 【実践①】pandas+openpyxlでExcelレポートを自動生成するコード
- 【実践②】matplotlibでグラフを自動生成するコード
- 【実践③】reportlabでPDFレポートを生成するコード
- 【実践④】定期自動実行とメール送信の設定
- AIを使ったコード生成の正しい使い方(2026年必須スキル)
- よくあるエラーと対処法
- よくある質問Q&A
「ExcelにはVBAがあるのに、なぜPythonを学ぶ必要があるの?」という疑問は至極まっとうです。2026年現在の正直な回答を先に言います。
- 既存のExcelブックの中でマクロを完結させたい
- Excelのボタン操作でチームメンバーが使えるようにしたい
- Pythonを学ぶ時間的余裕がない・短期間で解決したい
- Excel以外のファイル・システムとの連携が不要
- 会社のPCにPythonをインストールできない環境
- 複数のExcelファイルを横断して集計・統合したい
- データベース・API・WebサービスからデータをExcelに取り込みたい
- PDFやCSVなど複数のファイル形式を一括処理したい
- 将来的にデータ分析・機械学習に発展させたい
- AIツール(ChatGPT等)と組み合わせた自動化をしたい
「どちらを学ぶべきか迷っている」という方への結論:すでに職場でExcelを使いこなしている方はVBAから始め、DataサイエンスやAPI連携など幅広い活用を目指す方はPythonを選ぶのがおすすめです。2026年はExcel内でPythonを動かせる機能も実用化されており、VBAとPythonの垣根が低くなっています。
2023年後半から始まったMicrosoftの「Excel内でPythonを実行できる機能」が2026年現在では多くのMicrosoft 365ユーザーに提供されています。セルの数式にPythonコードを書くと、Pandas・Matplotlib・NumPy等がそのまま動作します。
- インストール不要でセルに直接Pythonを書いてデータ分析・グラフ作成ができる
- pandas・Matplotlib・NumPy等の主要ライブラリがそのまま使える
- Excelのデータ範囲を直接Pythonコードで参照できる
- Pythonで作成したグラフをExcelシート上に貼り付けられる
- コードはMicrosoftのクラウドで実行されるため高スペックPCが不要
ただしExcel内のPython機能はクラウド実行のためインターネット接続が必要で、定期自動実行・メール送信・他システムとの連携はできません。「まず試したい」入門向けとして最適で、本格的な業務自動化には後述のローカル環境構築が必要です。
import matplotlib.pyplot as plt
import openpyxl
print(“環境OK!バージョン:”, pd.__version__)
| ライブラリ名 | 用途 | インストール方法 |
|---|---|---|
| pandas | データ読み込み・集計・加工(Excelライクな操作) | Anacondaに含まれる |
| openpyxl | Excelファイルの読み書き・書式設定・グラフ作成 | pip install openpyxl |
| matplotlib | グラフ・チャートの生成(PNG/PDF形式で保存可) | Anacondaに含まれる |
| reportlab | PDFファイルの生成・表・テキスト配置 | pip install reportlab |
| schedule | Pythonスクリプトの定期自動実行 | pip install schedule |
| NumPy | 数値計算・配列処理 | Anacondaに含まれる |
最初のステップとして、Excelファイルからデータを読み込み、集計してレポートシートを自動生成するコードを作成します。まずシンプルな構造から始め、徐々に機能を追加するアプローチが挫折しないコツです。
from datetime import datetime
# ① 既存のExcelファイルを読み込む
df = pd.read_excel(‘売上データ.xlsx’)
# ② データを確認する(最初に必ずやること)
print(df.head()) # 先頭5行を表示
print(df.columns) # 列名を確認
print(df.dtypes) # 各列のデータ型を確認
# ③ 日別・商品別・支店別に集計
daily = df.groupby(‘日付’)[‘売上金額’].sum().reset_index()
product = df.groupby(‘商品’)[‘売上金額’].sum().sort_values(ascending=False).reset_index()
branch = df.groupby(‘支店’)[‘売上金額’].sum().sort_values(ascending=False).reset_index()
# ④ 複数シートのExcelに書き出す
filename = f’売上レポート_{datetime.now().strftime(“%Y%m%d”)}.xlsx’
with pd.ExcelWriter(filename, engine=‘openpyxl’) as writer:
daily.to_excel(writer, sheet_name=‘日別’, index=False)
product.to_excel(writer, sheet_name=‘商品別’, index=False)
branch.to_excel(writer, sheet_name=‘支店別’, index=False)
df.to_excel(writer, sheet_name=‘元データ’, index=False)
print(f”完了: {filename}”)
from openpyxl.utils import get_column_letter
import openpyxl
def format_sheet(ws):
“””シートのヘッダーを青くして列幅を自動調整する”””
# ヘッダー行を青字・白文字・中央揃えに
for cell in ws[1]:
cell.font = Font(bold=True, color=“FFFFFF”)
cell.fill = PatternFill(start_color=“1F4788”, end_color=“1F4788”, fill_type=“solid”)
cell.alignment = Alignment(horizontal=“center”)
# 列幅を内容に合わせて自動調整
for col in ws.columns:
max_len = max(len(str(cell.value or “”)) for cell in col)
ws.column_dimensions[get_column_letter(col[0].column)].width = max_len * 1.3
# 全シートに書式を適用
wb = openpyxl.load_workbook(filename)
for sheet_name in wb.sheetnames:
format_sheet(wb[sheet_name])
wb.save(filename)
print(“書式設定完了”)
「ファイルが見つからない」エラーが出た場合は、Pythonのスクリプトと同じフォルダに「売上データ.xlsx」を置くか、絶対パス(例:C:\\Users\\YourName\\Desktop\\売上データ.xlsx)で指定してください。
import matplotlib
matplotlib.rcParams[‘font.family’] = ‘DejaVu Sans’ # 日本語フォント未設定の場合の対処
fig, axes = plt.subplots(2, 2, figsize=(14, 9))
fig.suptitle(‘Sales Analysis Report’, fontsize=16, fontweight=‘bold’)
# ① 折れ線グラフ:日別売上推移
daily = df.groupby(‘日付’)[‘売上金額’].sum()
axes[0,0].plot(daily.index, daily.values, marker=‘o’, color=‘#3b82f6’)
axes[0,0].set_title(‘Daily Sales Trend’)
axes[0,0].tick_params(axis=‘x’, rotation=45)
# ② 棒グラフ:商品別売上
prod = df.groupby(‘商品’)[‘売上金額’].sum().sort_values(ascending=False)
axes[0,1].bar(prod.index, prod.values, color=‘#10b981’)
axes[0,1].set_title(‘Sales by Product’)
# ③ 円グラフ:支店別構成比
branch = df.groupby(‘支店’)[‘売上金額’].sum()
axes[1,0].pie(branch.values, labels=branch.index, autopct=‘%1.1f%%’)
axes[1,0].set_title(‘Share by Branch’)
# ④ 箱ひげ図:週別売上分布
df[‘週’] = df[‘日付’].dt.isocalendar().week
weekly = [g[‘売上金額’].values for _, g in df.groupby(‘週’)]
axes[1,1].boxplot(weekly)
axes[1,1].set_title(‘Weekly Distribution’)
plt.tight_layout()
plt.savefig(‘sales_chart.png’, dpi=200, bbox_inches=‘tight’)
plt.close()
print(“グラフ保存完了: sales_chart.png”)
日本語が「□□□」と文字化けする場合は、Windowsならmatplotlib.rcParams['font.family'] = 'MS Gothic'、Macなら'Hiragino Sans'に変更してください。または英語の軸ラベル・タイトルを使う方法が最も確実です。
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.units import inch
import os
def create_pdf_report(df, chart_path=‘sales_chart.png’):
filename = f’report_{datetime.now().strftime(“%Y%m%d”)}.pdf’
doc = SimpleDocTemplate(filename, pagesize=A4)
styles = getSampleStyleSheet()
elements = []
# タイトル
elements.append(Paragraph(“Monthly Sales Report”, styles[‘Title’]))
elements.append(Paragraph(f”Created: {datetime.now().strftime(‘%Y-%m-%d’)}”, styles[‘Normal’]))
elements.append(Spacer(1, 12))
# KPIサマリーテーブル
summary = [
[“Metric”, “Value”],
[“Total Sales”, f”¥{df[‘売上金額’].sum():,.0f}”],
[“Avg per Transaction”, f”¥{df[‘売上金額’].mean():,.0f}”],
[“Records”, f”{len(df):,} rows”],
]
tbl = Table(summary, colWidths=[3*inch, 3*inch])
tbl.setStyle(TableStyle([
(‘BACKGROUND’, (0,0), (-1,0), colors.HexColor(‘#1f4788’)),
(‘TEXTCOLOR’, (0,0), (-1,0), colors.white),
(‘FONTNAME’, (0,0), (-1,0), ‘Helvetica-Bold’),
(‘ALIGN’, (0,0), (-1,-1), ‘CENTER’),
(‘GRID’, (0,0), (-1,-1), 0.5, colors.grey),
(‘ROWBACKGROUNDS’, (0,1), (-1,-1), [colors.white, colors.lightgrey]),
]))
elements.append(tbl)
elements.append(Spacer(1, 20))
# グラフ画像を挿入
if os.path.exists(chart_path):
elements.append(Paragraph(“Sales Charts”, styles[‘Heading2’]))
elements.append(Image(chart_path, width=5.5*inch, height=3.5*inch))
doc.build(elements)
print(f”PDF完了: {filename}”)
return filename
create_pdf_report(df)
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
def generate_and_send():
“””レポート生成→メール送信”””
# 1. データ取得(実際はDB・APIに変更)
df = pd.read_excel(‘latest_sales.xlsx’)
# 2. レポート生成
create_sales_charts(df)
pdf_path = create_pdf_report(df)
# 3. メール送信
send_email(
subject = f”Daily Report {datetime.now().strftime(‘%Y-%m-%d’)}”,
body = “Please see the attached daily report.”,
attachments = [pdf_path, ‘sales_chart.png’],
recipients = [“boss@company.com”, “team@company.com”]
)
def send_email(subject, body, attachments, recipients):
# Gmailのアプリパスワードを使う(通常のパスワードではなくアプリパスワードが必要)
sender = “your_email@gmail.com”
password = os.environ.get(“GMAIL_APP_PASSWORD”) # 環境変数から取得(安全)
msg = MIMEMultipart()
msg[‘From’] = sender
msg[‘To’] = “, “.join(recipients)
msg[‘Subject’] = subject
msg.attach(MIMEText(body, ‘plain’))
for fp in attachments:
if os.path.exists(fp):
with open(fp, ‘rb’) as f:
part = MIMEBase(‘application’, ‘octet-stream’)
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header(‘Content-Disposition’, f’attachment; filename={os.path.basename(fp)}’)
msg.attach(part)
with smtplib.SMTP(‘smtp.gmail.com’, 587) as s:
s.starttls()
s.login(sender, password)
s.send_message(msg)
print(“メール送信完了”)
# スケジュール設定(毎朝9時・毎週月曜10時)
schedule.every().day.at(“09:00”).do(generate_and_send)
schedule.every().monday.at(“10:00”).do(generate_and_send)
if __name__ == “__main__”:
print(“スケジューラー起動(Ctrl+Cで停止)”)
while True:
schedule.run_pending()
time.sleep(60)
- GmailはGoogleアカウントの「アプリパスワード」(2段階認証設定後に発行)が必要です。通常のパスワードでは送信できません
- パスワードをコードの中に直接書くのは危険です。
os.environ.get("GMAIL_APP_PASSWORD")のように環境変数から取得するか、.envファイルに記録してpython-dotenvライブラリで読み込む方法を使ってください - コードをGitHubに公開する場合は特に注意が必要です。パスワードが含まれたコードを公開するとアカウントが乗っ取られるリスクがあります
- 「このExcelを読んで商品別に集計するPythonコードを書いて」→ファイルの列名・やりたいことを具体的に伝えると精度が高い
- 「このエラーメッセージの意味は何?どう直せばいい?」→エラーメッセージをそのままコピペしてAIに貼り付けると即解決することが多い
- 「このコードの何行目が何をしているか日本語で説明して」→コードを理解するための使い方。学習効率が大幅に上がる
- 「上のコードにメール送信機能を追加して」→既存のコードを貼り付けて機能追加を依頼する。会話の続きとして改善を積み重ねられる
- 注意:AIが生成したコードは必ず実行前に内容を読む→意図と異なる動作をすることがある。「何をするコードか」を理解してから実行すること
2026年現在、「Pythonを完全に習得してから自動化に着手する」必要はなくなりました。「やりたいことをAIに伝える→コードを生成してもらう→実行して確認→エラーが出たらAIに聞く」というサイクルで、プログラミング経験が浅い方でも業務自動化に着手できます。ただしAIが生成したコードを「ブラックボックスのまま使う」状態では、エラーへの対応・改修ができません。コードの意味を少しずつ理解しながら使うことを推奨します。
| エラー | 原因 | 解決策 |
|---|---|---|
FileNotFoundError: [Errno 2] |
Excelファイルが見つからない | スクリプトと同じフォルダにファイルを置くかos.path.abspath('ファイル名')で絶対パスを取得して指定 |
PermissionError: [Errno 13] |
対象のExcelファイルが開きっぱなし | Excelを閉じてから実行する。または別のファイル名で保存する |
UnicodeDecodeError |
日本語CSVを読み込む際の文字化け | pd.read_csv('file.csv', encoding='cp932')(Shift-JIS)またはencoding='utf-8-sig'(BOM付きUTF-8)を指定 |
KeyError: '列名' |
存在しない列名を指定している | print(df.columns)で実際の列名を確認してからコードを修正 |
ModuleNotFoundError |
ライブラリがインストールされていない | pip install ライブラリ名でインストール。Anaconda環境の場合conda install ライブラリ名でもよい |
| 日本語が□□□になる(文字化け) | matplotlibの日本語フォント未設定 | Windows:plt.rcParams['font.family'] = 'MS Gothic'Mac: 'Hiragino Sans' |
import glob; files = glob.glob('reports/*.xlsx')df = pd.concat([pd.read_excel(f) for f in files], ignore_index=True)これだけで100ファイルを数秒で一括統合できます。VBAで同じことをすると数分かかる処理がPythonでは数秒になるケースも多く、大量ファイル処理はPythonが圧倒的に有利です。
- VBAとPythonの使い分け:Excel内で完結するなら VBA・複数システム連携・PDF生成・大量ファイル処理ならPython。2026年はExcel内でもPythonが使えるようになった
- セットアップはAnaconda一択。インストール後に
pip install openpyxl reportlab scheduleを追加で完了 - 基本フロー:pandas でデータ読み込み→groupby で集計→ExcelWriter で書き出し→openpyxl で書式設定
- グラフはmatplotlib(PNG保存)→PDFへはreportlabでグラフ画像+表を埋め込んで生成
- 2026年の必須スキル:AIにエラーメッセージを貼り付けて解決・やりたいことをAIに伝えてコードを生成してもらうAI活用サイクル
- パスワードはコードに直書きしない。環境変数(
os.environ)か.envファイルを使って安全に管理する
※本記事のコードはPython 3.10以上・pandas 2.x・openpyxl 3.x・reportlab 4.x・schedule 1.x での動作を想定しています。ライブラリのバージョンによって動作が異なる場合があります。
