PythonでExcel作業を自動化する
毎月の転記・集計・体裁づくりをPythonに任せるための実践ガイドです。openpyxl と pandas の担当範囲を先に切り分けてから、実行して確認した最小コードを5本渡します。
1. 自動化できる作業・できない作業
「支店ごとのファイルを開いてコピーし、1枚に貼り、合計を出して提出する」。手順が決まっていて判断が要らず、回数が多い作業ほどPythonが効きます。ただしExcelの機能が何でも置き換わるわけではないので、先に線を引きます。
| 置き換えやすい作業 | openpyxl・pandasだけでは扱えない作業 |
|---|---|
| 複数ファイル・複数シートの結合 | VBAマクロの実行 |
| 条件での抽出・並べ替え・集計 | 数式の再計算(下記) |
| 決まった体裁のレポート生成 | パスワード保護ブックの読み込み |
| CSVとExcelの相互変換 | ピボットテーブルの新規作成 |
openpyxl は =SUM(B2:B4) のような数式を書き込めますが、計算するのはExcel本体です。検証では、書き込んだ直後のファイルを data_only=True で読み直すと数式セルの値は None でした。数式を入れてPythonで結果を読む処理は1往復では成立しません。結果まで欲しいなら計算済みの値を書き込みます。
2. 結論:openpyxl と pandas の違いと使い分け
セル単位の話(書式・色・幅・数式・シート操作)なら openpyxl。行と列の話(抽出・集計・結合)なら pandas。両方必要なら、pandas で中身を作って保存し、openpyxl で開き直して整える——この順番が手数を減らせます。
| やりたいこと | 担当 | 理由 |
|---|---|---|
| 見出しを太字・背景色に/列幅・表示形式を変える | openpyxl | 装飾は pandas の守備範囲外 |
| 数式を書き込む/ウィンドウ枠を固定する | openpyxl | ファイルの構造そのものを組み立てられる |
| 雛形ブックの決まった位置に値を差し込む | openpyxl | 他のセルを保ったまま書き換えられる |
| 条件で絞り込む/グループごとに合計する | pandas | 1行で書ける。数え間違いが減る |
| 複数ファイル・複数シートを縦に積む | pandas | concat が列名を合わせて連結する |
| CSVとExcelを行き来する | pandas | read_csv と to_excel が同じ書き方 |
pandas で .xlsx を読み書きすると内部で openpyxl が呼ばれます。pandas を選んでも openpyxl は必要です。
3. 準備と対応ファイル形式
pip install openpyxl pandas
Python自体がまだならインストール手順からどうぞ。
Windows 11 / Python 3.12.10 / openpyxl 3.1.5 / pandas 3.0.3。掲載コードはこの環境で実行し、出力を確認しました。同日のPyPIでの最新版は openpyxl 3.1.5(2024年6月28日リリース・対応Pythonは3.8以上)、pandas 3.0.6(対応Pythonは3.11以上)です。なお openpyxl 公式ドキュメントのトップには 3.1.3 という版表記が残っており、配布版と一致していません。
pandas の read_excel はエンジンを指定しないと拡張子から自動で選びます(公式ドキュメントの記載)。
| 拡張子 | pandas が既定で使うエンジン | 必要なパッケージ |
|---|---|---|
| .xlsx / .xlsm | openpyxl | openpyxl |
| .xls(古い形式) | xlrd | xlrd 2.0.1以上(読み取り専用) |
| .xlsb(バイナリ形式) | pyxlsb | pyxlsb |
| .ods(OpenDocument) | odf | odfpy |
書き込み側は openpyxl か xlsxwriter(.ods は odfpy)で、主な対象は .xlsx です。.xls 形式での保存はできません。
以降のコードの材料は sales.xlsx です。1行目が見出し(日付/支店/商品/数量/金額)で、シート「7月」に6行、「8月」に2行。手元で再現するならExcelで同じ列の表を作り、日付列は日付として入力してください(文字列にすると第7章(3)の話になります)。
4. openpyxl の最小コード2本
① 既存ブックを読む
シートは名前で取り出し、セルは ws["A1"](Excelと同じ番地)か ws.cell(row=2, column=2)(行・列の番号)で指定します。番号は1始まりで、リストの0始まりとは違います。
from openpyxl import load_workbook
wb = load_workbook("sales.xlsx", data_only=True)
ws = wb["7月"]
print("行数:", ws.max_row, "列数:", ws.max_column)
print("A1:", ws["A1"].value, "/ B2:", ws.cell(row=2, column=2).value)
for row in ws.iter_rows(min_row=2, values_only=True):
hizuke, shiten, shohin, suryo, kingaku = row
print(f"{hizuke:%Y-%m-%d} {shiten} {shohin} {suryo}個 {kingaku}円")
wb.close()
実行すると「行数: 7 列数: 5」に続き、見出しを除く6行が 2026-07-01 東京 ノートA 12個 1200円 の形で並びました。min_row=2 が見出しを飛ばす指定、values_only=True が値だけを受け取る指定です。日付セルは datetime で返るため f"{hizuke:%Y-%m-%d}" で書式を指定できます。
② 書式を付けて新しいブックを作る
見出しの色、桁区切り、枠の固定まで体裁をコードで決められます(列幅は ws.column_dimensions["A"].width = 12)。
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill
wb = Workbook()
ws = wb.active
ws.title = "支店別集計"
ws.append(["支店", "金額"])
for row in [("東京", 3950), ("大阪", 3800), ("名古屋", 500)]:
ws.append(row)
# 見出し行を太字・白文字・背景色に
for cell in ws[1]:
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = PatternFill("solid", fgColor="4472C4")
# 金額列を3桁区切りに
for row in range(2, ws.max_row + 1):
ws.cell(row=row, column=2).number_format = "#,##0"
# 合計行(Excelの数式を文字列として書き込む)
last = ws.max_row
ws.cell(row=last + 1, column=1, value="合計").font = Font(bold=True)
ws.cell(row=last + 1, column=2, value=f"=SUM(B2:B{last})").number_format = "#,##0"
ws.freeze_panes = "A2"
wb.save("summary.xlsx")
print("保存しました:", ws.max_row, "行")
出力は 保存しました: 5 行。読み直すと、A1が太字、B4の表示形式が #,##0、固定枠が A2、B5の値は文字列 '=SUM(B2:B4)' でした。ws[1] は1行目のセル全部を指すので、列数が増えても書き換え不要です。number_format はExcelの「セルの書式設定」と同じ表記で、日付なら "yyyy/mm/dd"、百分率なら "0.0%" と書けます。
5. pandas の最小コード2本
③ 読んで、集計して、書き出す
「支店ごとの合計を出す」処理は pandas なら1行です。for文で辞書に足し込むより数え間違いが起きにくくなります。
import pandas as pd
df = pd.read_excel("sales.xlsx", sheet_name="7月")
print(df.dtypes)
# 支店ごとの合計金額(降順)
by_branch = (df.groupby("支店", as_index=False)["金額"]
.sum()
.sort_values("金額", ascending=False))
print(by_branch)
# 条件で絞り込む
tokyo = df[(df["支店"] == "東京") & (df["金額"] >= 1000)]
print("東京かつ1000円以上:", len(tokyo), "件")
by_branch.to_excel("by_branch.xlsx", sheet_name="支店別", index=False)
出力は、支店別合計が「東京 3950 / 大阪 3800 / 名古屋 500」、絞り込みが「東京かつ1000円以上: 2 件」でした。index=False を付けないと0,1,2…の通し番号がA列に入るため、提出用ではほぼ必須の指定です。
検証環境(pandas 3.0.3)では文字列の列が str、日付の列が datetime64[us] と表示されました。pandas 2系の解説記事では object・datetime64[ns] です。表示が違っても中身は同じなので、古い記事と一致しなくても問題ありません。
④ 複数シートをまとめて読む・書く
sheet_name=None を渡すと、全シートが「シート名がキーの辞書」で返ります。書き出しは ExcelWriter を with で開けば1つのブックに何枚でもシートを作れます。
import pandas as pd
# 全シートをまとめて読む(シート名がキーの辞書で返る)
sheets = pd.read_excel("sales.xlsx", sheet_name=None)
# シート名を列として残したまま縦に連結
merged = pd.concat(
[sheet_df.assign(月=name) for name, sheet_df in sheets.items()],
ignore_index=True,
)
print("連結後:", merged.shape)
# 1つのブックに複数シートで書き出す
with pd.ExcelWriter("report.xlsx", engine="openpyxl") as writer:
merged.to_excel(writer, sheet_name="明細", index=False)
merged.groupby("月", as_index=False)["金額"].sum().to_excel(
writer, sheet_name="月別集計", index=False)
出力は 連結後: (8, 6)(7月6行+8月2行に月の列が加わった形)。report.xlsx には「明細」「月別集計」の2シートが入り、月別集計は7月8,250円・8月3,500円でした。assign(月=name) でどのシート由来かを列として残すのがポイントで、これが無いと差分を追うときに手が止まります。
6. 複数ファイルを1枚にまとめる
支店から届いた月次ファイルを束ねる定番作業です。pathlib を使えば、OSごとのパス区切り文字を意識せずに書けます。
from pathlib import Path
import pandas as pd
frames = []
for path in sorted(Path("月次データ").glob("*.xlsx")):
if path.name.startswith("~$"): # Excelで開いている間にできる一時ファイル
continue
part = pd.read_excel(path)
part["ファイル名"] = path.name # どのファイル由来か残す
frames.append(part)
print(f"読み込み: {path.name} ({len(part)}行)")
all_df = pd.concat(frames, ignore_index=True)
all_df.to_excel("全社まとめ.xlsx", index=False)
print("まとめ:", all_df.shape)
3ファイル(東京7月・大阪7月・東京8月/中身は日付・商品・数量・金額の4列で、支店名はファイル名側)のフォルダで実行すると各ファイルの行数が表示され、まとめ: (5, 5) と 全社まとめ.xlsx が出力されました。~$ で始まるファイルを飛ばすのは、誰かがExcelで開いている間だけ現れる一時ファイルを読みにいく事故を防ぐためです。共有フォルダ相手ではこの1行の有無で成功率が変わります。
体裁を付けたいときは、to_excel のあとに第4章②のスタイル指定を load_workbook で適用します。pandas で作って保存 → openpyxl で開き直して装飾の2段構えです。
上のコードで12ファイル×各200行(合計2,400行)を結合して書き出す処理を3回計測したところ、検証環境では0.29秒・0.29秒・0.47秒でした。手作業の所要時間は人と環境で大きく変わり、同条件で計測していないため倍率は出しません。判断材料は「2,400行の結合が1秒未満」の一点で、あとは同じ作業が月に何回あるかを掛けてください。回数が多く手順が決まった作業から順に自動化すると回収が早くなります。
7. つまずきやすいエラーと落とし穴5点
以下は検証環境でわざと再現させた症状と、記録したメッセージです。エラー文の読み方はよくあるエラー解決ページも参考になります。
(1) openpyxl が入っていない
ImportError: `Import openpyxl` failed. Use pip or conda to install the openpyxl package.
pandas だけを入れて read_excel を呼ぶと出ます。「pandasを入れたのにExcelが読めない」の原因はほぼこれで、pip install openpyxl で解決します。
(2) .xls と .xlsx を取り違える
中身が古い .xls 形式のファイルを渡すと、xlrd の導入を促すメッセージが出ます。
ImportError: `Import xlrd` failed. Install xlrd >= 2.0.1 for xls Excel support …
openpyxl で直接開くと InvalidFileException: openpyxl does not support the old .xls file format になります。検証で .xlsx の拡張子だけを .xls に書き換えたところ、pandas は中身を見て判定するため問題なく読めました(6行5列を取得)。一方 openpyxl は拡張子を見てエラーにします。拡張子と中身が一致しないファイルは実務で出回ります。読めたり読めなかったりするときは中身の形式を疑ってください。
(3) 日付が5桁の数字になる
Excelは日付を「1899年12月30日からの通算日数」で保持します。検証では、同じ 46204 を入れたセルでも結果が分かれました(文字列で入れた日付は文字列のまま返ります)。
| セルの状態 | openpyxl が返す値 |
|---|---|
date(2026, 7, 1) を書き込んだ | datetime(2026, 7, 1, 0, 0) |
数値 46204・表示形式は標準 | 46204(整数のまま) |
数値 46204・表示形式が yyyy/mm/dd | datetime(2026, 7, 1, 0, 0) |
判定材料はセルの表示形式で、標準のままだと日付のつもりの列が数字で返ります。数字で来た列は次の1行で戻せます(検証では 46204 が 2026-07-01 になり、datetime(1899, 12, 30) + timedelta(days=46204) とも一致)。同じ列に日付と文字列が混ざると型が object になり日付計算が通らなくなるので、読み込んだ直後にそろえます。
df["日付"] = pd.to_datetime(df["日付"], unit="D", origin="1899-12-30")
(4) PermissionError で保存できない
PermissionError: [Errno 13] Permission denied
保存先がロックされた状態で wb.save() を呼ぶと出ます(検証ではファイルロックをかけて再現)。実務では出力先を自分がExcelで開いたまま実行したケースが大半で、閉じて再実行すれば通ります。
(5) 大きいファイルで時間とメモリを食う
数万行を超えると読み込みが重くなります。openpyxl の流し読み load_workbook("sales.xlsx", read_only=True) と、pandas の read_excel("sales.xlsx", usecols=["支店", "金額"], nrows=3) で必要な分だけ読めます(どちらも検証環境で動作確認済み)。read_only=True では書き込みができず、使い終わったら wb.close() で閉じます。
8. 動く実例と次の一歩
断片が動いたら、次は「画面のあるツール」に仕立てる段階です。この記事の技術を使った中級者向けアプリの解説記事(完成コードつき)があります。
| 記事 | この記事との対応 |
|---|---|
| Excelレポート生成 | 第4章②の書式つき書き出しをGUI化 |
| CSVデータビューア・エディタ | 第5章③の読み込みと絞り込みを画面で操作 |
| CSVデータクリーナー | 結合前の下ごしらえ(欠損値・重複) |
| 家計簿ダッシュボード | 第5章④の集計結果をグラフに |
| アンケート集計ツール | 回答データの度数集計とグラフ化 |
| ログファイルアナライザー | 正規表現での抽出とフィルタ表示 |
ライブラリ全体はよく使うPythonライブラリ51選、学習の順序は入門書を終えたら次に作るもの、題材は中級者向けアプリ一覧にあります。
Excelが片付くと、次はPDF・メール・ファイル整理が目につきます。
一冊で通すなら業務自動化の定番書『退屈なことはPythonにやらせよう 第3版』(オライリー・ジャパン・2026年3月刊・800ページ)。Excel操作に加え、SQLiteでの保存、Playwrightでのブラウザ操作、OCRによる画像内の文字認識まで扱います。
当サイト独自の基準によるおすすめ本ランキングでは総合3位で、参考書比較ページの該当欄で対象レベルと収録内容を確認できます。リンク先は当サイトの書籍紹介ページで、アフィリエイト広告を含みます。第2版とは収録内容が異なるため版をご確認ください。中身の詳細は第3版のレビュー記事にあります。
本を買わずに進めるなら、今日のスクリプトを来月も使える形に整えるのが確実な前進です。ファイル名を引数で受け取る、出力先に日付を入れる——この2つで「今月だけ動いたコード」が「毎月動くツール」になります。
9. よくある質問(FAQ)
Q. openpyxl と pandas、どちらを先に覚えるべきですか
目的で決めてください。表の体裁(色・幅・数式)を整えるのが中心なら openpyxl から、複数のファイルやシートを集計するのが中心なら pandas からが近道です。どちらでも、.xlsx を扱う限り openpyxl は必要です。
Q. VBAで作った既存のマクロをPythonから動かせますか
できません。どちらもファイルの中身を読み書きするライブラリで、Excel本体を操作する機能を持たないためです。マクロ実行や画面操作には、Excelを外部から動かす別の仕組み(pywin32 など)が要ります。当サイトでは未検証のため本記事では扱いません。
Q. Excelがインストールされていないパソコンでも動きますか
動きます。両者は .xlsx の中身を直接読み書きする仕組みで、Excel本体を呼び出しません。ただし数式の計算はExcelの仕事なので、書き込んだ数式の結果はExcel(または互換ソフト)で開くまで確定しません。
本記事は、生成AIを活用して下書きし、運営者が内容を確認・編集したうえで公開しています。AIの利用方針は免責事項をご覧ください。
10. まとめ
- セルを触るなら openpyxl、表を触るなら pandas。両方必要なら pandas で作って openpyxl で装飾する
- pandas で .xlsx を扱うと内部で openpyxl が使われる。
pip install openpyxl pandasの2つを入れておく - 数式は書き込めるが計算はExcelの仕事。結果まで欲しいなら計算済みの値を書き込む
- 日付が5桁の数字で返るのはセルの表示形式が原因。
pd.to_datetime(..., unit="D", origin="1899-12-30")で戻せる - 2,400行の結合は検証環境で1秒未満。回数が多く手順が決まった作業から自動化すると回収が早い
まず職場のファイルで第4章①の読み込みを試してください。列名と行数が想定どおり取れれば、あとは組み合わせです。