プログラミング / 言語 / Python
Excel 書き込み
— openpyxl / pandas 完全ガイド
1. 新規ブックを作成する
openpyxl で新しい Excel ファイルを作成するには Workbook() でブックを生成し、save() で保存します。
import openpyxl
# ── 新規ブックを作成 ──────────────────────────────────────
wb = openpyxl.Workbook()
# デフォルトでアクティブシート("Sheet")が作成される
ws = wb.active
ws.title = "売上データ" # シート名を変更
# ── シートを追加・削除 ─────────────────────────────────
ws2 = wb.create_sheet("設定") # 末尾に追加
ws3 = wb.create_sheet("サマリー", 0) # 先頭(index=0)に追加
# シートを削除
del wb["設定"]
# ── シートをコピー ─────────────────────────────────────
ws_copy = wb.copy_worksheet(ws) # 同じブック内でコピー
ws_copy.title = "売上データ_コピー"
# ── 保存 ──────────────────────────────────────────────
wb.save("output.xlsx")
print("保存完了")
2. セルに値を書き込む
セルへの書き込みは A1 形式または cell(row, column) で行います。リストや辞書を使った一括書き込みも便利です。
import openpyxl
from datetime import datetime, date
wb = openpyxl.Workbook()
ws = wb.active
# ── A1 形式で書き込み ────────────────────────────────────
ws["A1"] = "名前"
ws["B1"] = "年齢"
ws["C1"] = "売上"
ws["D1"] = "登録日"
# ── cell() メソッドで書き込み ───────────────────────────
ws.cell(row=2, column=1, value="田中太郎")
ws.cell(row=2, column=2, value=30)
ws.cell(row=2, column=3, value=520000)
ws.cell(row=2, column=4, value=date(2026, 1, 15))
# ── データをリストで一括書き込み(append) ───────────────
# append() は現在の最終行の次の行に追加する
data = [
("鈴木花子", 25, 380000, date(2026, 2, 10)),
("佐藤次郎", 35, 670000, date(2026, 3, 5)),
("山田美咲", 28, 430000, date(2026, 4, 20)),
]
for row in data:
ws.append(row)
# ── 2 次元リストをまとめて書き込む ─────────────────────
rows_data = [
["商品A", 100, 2500],
["商品B", 80, 3200],
["商品C", 150, 1800],
]
start_row = 10
for r_idx, row in enumerate(rows_data, start=start_row):
for c_idx, val in enumerate(row, start=1):
ws.cell(row=r_idx, column=c_idx, value=val)
wb.save("output.xlsx")
append() の使いどころ: 行数が決まっていないデータを逐次追加する場合、
append() が便利です。現在の最終行の次に自動で追加されるため、行インデックスを管理する必要がありません。
3. フォント・スタイルを設定する
openpyxl.styles.Font でフォントの種類・サイズ・太字・色などを設定できます。
import openpyxl
from openpyxl.styles import Font
wb = openpyxl.Workbook()
ws = wb.active
# ── フォントを設定 ────────────────────────────────────────
# ヘッダー行に太字・フォントサイズ 12・白文字を設定
header_font = Font(
name="游ゴシック", # フォント名
size=12, # サイズ(pt)
bold=True, # 太字
italic=False, # 斜体
underline="single", # 下線: 'single' / 'double' / None
color="FFFFFF", # 文字色(RRGGBB)
strike=False, # 取り消し線
)
ws["A1"] = "名前"
ws["A1"].font = header_font
# ── まとめて設定(ヘッダー行 A1:D1) ─────────────────────
headers = ["名前", "年齢", "売上", "登録日"]
for col_idx, header in enumerate(headers, start=1):
cell = ws.cell(row=1, column=col_idx, value=header)
cell.font = Font(bold=True, size=11, color="1F3864")
# ── 色指定の方法 ─────────────────────────────────────────
from openpyxl.styles import Font
from openpyxl.styles.colors import Color
# 16 進数の RRGGBB(アルファなし)
red_font = Font(color="FF0000")
# テーマカラー(Excel のテーマに依存)
# theme_font = Font(color=Color(theme=4, tint=-0.25))
ws["A2"] = "データ行"
ws["A2"].font = Font(size=10, color="333333")
wb.save("output.xlsx")
4. 塗りつぶし(Fill)を設定する
PatternFill でセルの背景色を設定します。ヘッダー行の色分けや、条件に応じた行のハイライトに使います。
import openpyxl
from openpyxl.styles import PatternFill, Font
wb = openpyxl.Workbook()
ws = wb.active
# ── PatternFill で塗りつぶし ──────────────────────────────
header_fill = PatternFill(
fill_type="solid", # 塗りつぶしの種類(solid が最も一般的)
start_color="1F3864", # 前景色(solid では背景色として機能)
end_color="1F3864" # 後景色(solid では同じ値を設定)
)
# ヘッダー行(A1:D1)に背景色と白文字を設定
headers = ["名前", "年齢", "売上", "登録日"]
for col_idx, header in enumerate(headers, start=1):
cell = ws.cell(row=1, column=col_idx, value=header)
cell.fill = header_fill
cell.font = Font(bold=True, color="FFFFFF")
# ── 交互行(ゼブラ模様)────────────────────────────────
data = [("田中", 30, 520000), ("鈴木", 25, 380000), ("佐藤", 35, 670000)]
light_fill = PatternFill(fill_type="solid", start_color="D9E1F2", end_color="D9E1F2")
for r_idx, row in enumerate(data, start=2):
for c_idx, val in enumerate(row, start=1):
cell = ws.cell(row=r_idx, column=c_idx, value=val)
if r_idx % 2 == 0: # 偶数行に色を付ける
cell.fill = light_fill
wb.save("output.xlsx")
fill_type の種類:
solid(単色塗りつぶし)が最もよく使われます。他に darkGrid・lightGrid・darkTrellis などのパターンも指定できます。
5. 罫線(Border)を設定する
Border と Side を組み合わせて、上下左右の罫線を設定します。
import openpyxl
from openpyxl.styles import Border, Side, PatternFill, Font
wb = openpyxl.Workbook()
ws = wb.active
# ── Side(線の種類・色)────────────────────────────────
# border_style の主な値: 'thin', 'medium', 'thick', 'dashed', 'dotted', 'double'
thin_side = Side(border_style="thin", color="000000")
thick_side = Side(border_style="thick", color="1F3864")
dash_side = Side(border_style="dashed", color="AAAAAA")
# ── 全周に細い罫線 ──────────────────────────────────────
all_border = Border(
left=thin_side, right=thin_side,
top=thin_side, bottom=thin_side
)
# ── 下線だけ ────────────────────────────────────────────
bottom_only = Border(bottom=thick_side)
# ── 外枠だけ(範囲に枠線をつける関数) ─────────────────
def apply_border_range(ws, min_row, max_row, min_col, max_col, side):
for row in ws.iter_rows(min_row=min_row, max_row=max_row,
min_col=min_col, max_col=max_col):
for cell in row:
left = side if cell.column == min_col else Side()
right = side if cell.column == max_col else Side()
top = side if cell.row == min_row else Side()
bottom = side if cell.row == max_row else Side()
cell.border = Border(left=left, right=right, top=top, bottom=bottom)
# ── データを書いて罫線を適用 ──────────────────────────
headers = ["名前", "年齢", "売上"]
for c, h in enumerate(headers, start=1):
cell = ws.cell(row=1, column=c, value=h)
cell.border = Border(bottom=thick_side, top=thin_side,
left=thin_side, right=thin_side)
data = [("田中", 30, 520000), ("鈴木", 25, 380000)]
for r, row in enumerate(data, start=2):
for c, val in enumerate(row, start=1):
ws.cell(row=r, column=c, value=val).border = all_border
wb.save("output.xlsx")
6. 配置(Alignment)を設定する
Alignment で文字の水平・垂直配置、折り返し、インデントを制御します。
import openpyxl
from openpyxl.styles import Alignment
wb = openpyxl.Workbook()
ws = wb.active
# ── Alignment のオプション ────────────────────────────────
# horizontal: 'left' / 'center' / 'right' / 'fill' / 'justify'
# vertical : 'top' / 'center' / 'bottom'
# wrap_text : True でセル内改行を有効化
# shrink_to_fit: True で自動縮小
# indent : 左インデント(整数)
# text_rotation: 文字の回転角度(0〜360)
center_align = Alignment(horizontal="center", vertical="center")
left_wrap = Alignment(horizontal="left", vertical="top", wrap_text=True)
right_align = Alignment(horizontal="right", vertical="center")
rotated = Alignment(text_rotation=45, horizontal="center", vertical="center")
ws["A1"] = "中央揃え"
ws["A1"].alignment = center_align
ws["B1"] = "折り返しあり\n2行目のテキスト"
ws["B1"].alignment = left_wrap
ws.row_dimensions[1].height = 40 # 折り返しのため行高さを確保
ws["C1"] = "右揃え"
ws["C1"].alignment = right_align
ws["D1"] = "45度回転"
ws["D1"].alignment = rotated
# ── 数値は右揃えが慣習 ──────────────────────────────────
for row in ws.iter_rows(min_row=2, max_row=10, min_col=2, max_col=4):
for cell in row:
cell.alignment = Alignment(horizontal="right")
wb.save("output.xlsx")
7. 表示形式を設定する
数値・日付・通貨の表示形式は cell.number_format で指定します。
| データ型 | フォーマット文字列 | 表示例 |
|---|---|---|
| 整数(3 桁区切り) | #,##0 |
1,234,567 |
| 小数(2 桁) | #,##0.00 |
1,234.56 |
| 円記号付き | ¥#,##0 |
¥1,234,567 |
| パーセント | 0.0% |
12.3% |
| 日付 | yyyy/mm/dd |
2026/05/26 |
| 日時 | yyyy/mm/dd hh:mm:ss |
2026/05/26 14:30:00 |
| 時刻 | hh:mm |
14:30 |
import openpyxl
from datetime import date, datetime
wb = openpyxl.Workbook()
ws = wb.active
ws["A1"] = 1234567
ws["A1"].number_format = "#,##0" # 1,234,567
ws["B1"] = 0.1234
ws["B1"].number_format = "0.0%" # 12.3%
ws["C1"] = 520000
ws["C1"].number_format = '¥#,##0' # ¥520,000
ws["D1"] = date(2026, 5, 26)
ws["D1"].number_format = "yyyy/mm/dd" # 2026/05/26
ws["E1"] = datetime(2026, 5, 26, 14, 30)
ws["E1"].number_format = "yyyy/mm/dd hh:mm" # 2026/05/26 14:30
wb.save("output.xlsx")
8. 数式を書き込む
Excel の数式は文字列として "=SUM(B2:B10)" の形式で書き込みます。
import openpyxl
wb = openpyxl.Workbook()
ws = wb.active
# ── ヘッダーとデータ ─────────────────────────────────────
ws.append(["月", "売上", "目標", "達成率"])
data = [("1月", 520000, 500000), ("2月", 380000, 450000), ("3月", 670000, 600000)]
for row in data:
ws.append(row)
# ── 数式セルを挿入 ───────────────────────────────────────
# 達成率(D列)= 売上 / 目標
for r in range(2, len(data) + 2):
ws.cell(row=r, column=4).value = f"=B{r}/C{r}"
ws.cell(row=r, column=4).number_format = "0.0%"
# ── 合計行 ──────────────────────────────────────────────
total_row = len(data) + 2
ws.cell(row=total_row, column=1, value="合計")
ws.cell(row=total_row, column=2, value=f"=SUM(B2:B{total_row-1})")
ws.cell(row=total_row, column=3, value=f"=SUM(C2:C{total_row-1})")
ws.cell(row=total_row, column=2).number_format = "#,##0"
ws.cell(row=total_row, column=3).number_format = "#,##0"
# ── よく使う数式の例 ────────────────────────────────────
ws["F1"] = "=MAX(B2:B4)" # 最大値
ws["F2"] = "=MIN(B2:B4)" # 最小値
ws["F3"] = "=AVERAGE(B2:B4)" # 平均
ws["F4"] = "=COUNT(B2:B4)" # カウント
ws["F5"] = "=COUNTA(A2:A4)" # 空でないセル数
ws["F6"] = '=IF(B2>500000,"達成","未達成")' # IF文
ws["F7"] = "=VLOOKUP(A2,A2:D4,2,FALSE)" # VLOOKUP
# ── 絶対参照 ────────────────────────────────────────────
ws["G1"] = "=B2/$B$5" # $B$5 は絶対参照
wb.save("output.xlsx")
注意: openpyxl で書き込んだ数式は、Excel でファイルを開いて再計算するまで値が確定しません。
data_only=True で開いても計算結果は取得できません。
9. 行・列の操作
列幅・行高さの設定、行・列の挿入・削除、非表示設定を解説します。
import openpyxl
wb = openpyxl.Workbook()
ws = wb.active
# ── 列幅を設定 ──────────────────────────────────────────
ws.column_dimensions["A"].width = 20 # A列を幅20に
ws.column_dimensions["B"].width = 10
ws.column_dimensions["C"].width = 15
# ── 行の高さを設定 ─────────────────────────────────────
ws.row_dimensions[1].height = 30 # 1行目の高さ30
ws.row_dimensions[2].height = 20
# ── 列を非表示 / 表示 ──────────────────────────────────
ws.column_dimensions["D"].hidden = True # D列を非表示
ws.row_dimensions[5].hidden = True # 5行目を非表示
# ── 行を挿入 ──────────────────────────────────────────
# 2行目の前に1行挿入(既存行が下に移動)
ws.insert_rows(2)
# ── 行を削除 ──────────────────────────────────────────
# 3行目を1行削除
ws.delete_rows(3)
# ── 複数行を挿入・削除 ────────────────────────────────
ws.insert_rows(5, amount=3) # 5行目から3行挿入
ws.delete_rows(5, amount=3) # 5行目から3行削除
# ── 列を挿入・削除 ────────────────────────────────────
ws.insert_cols(3) # C列の前に1列挿入
ws.delete_cols(3) # C列を1列削除
ws.insert_cols(2, amount=2) # B列の前に2列挿入
# ── 列幅を自動調整(ユーティリティ) ──────────────────
# openpyxl には自動調整機能がないため手動で設定
# フォントサイズ 11pt の場合の目安: 文字数 × 1.2 程度
for col in ws.columns:
max_len = 0
col_letter = col[0].column_letter
for cell in col:
if cell.value:
max_len = max(max_len, len(str(cell.value)))
ws.column_dimensions[col_letter].width = max(max_len * 1.2, 8)
wb.save("output.xlsx")
10. セルの結合・解除
merge_cells() で複数セルを結合し、unmerge_cells() で解除します。
import openpyxl
from openpyxl.styles import Alignment, Font, PatternFill
wb = openpyxl.Workbook()
ws = wb.active
# ── セルを結合 ──────────────────────────────────────────
# A1:D1 を結合(タイトル行として使う例)
ws.merge_cells("A1:D1")
# 結合後の値は左上セルに設定する
ws["A1"] = "月次売上レポート"
ws["A1"].alignment = Alignment(horizontal="center", vertical="center")
ws["A1"].font = Font(bold=True, size=14)
ws["A1"].fill = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
ws["A1"].font = Font(bold=True, size=14, color="FFFFFF")
ws.row_dimensions[1].height = 30
# ── 行範囲・列範囲の指定方法 ──────────────────────────
ws.merge_cells(start_row=3, start_column=1, end_row=3, end_column=2)
# ── 結合を解除 ─────────────────────────────────────────
ws.unmerge_cells("A1:D1")
ws.unmerge_cells(start_row=3, start_column=1, end_row=3, end_column=2)
# ── 結合されているセル一覧を確認 ──────────────────────
print(ws.merged_cells.ranges) # 結合範囲の一覧
wb.save("output.xlsx")
注意: 結合セルの左上以外のセルに値を設定しても保存されません。また、結合セルを含む行・列の挿入・削除は予期しない動作をすることがあります。なるべくデータ範囲と書式範囲は分けて管理しましょう。
11. 既存ファイルを編集する
既存の Excel ファイルを開いて一部を書き換え・追記する場合は load_workbook() で読み込み、保存します。
import openpyxl
from pathlib import Path
# ── 既存ファイルを開く ──────────────────────────────────
wb = openpyxl.load_workbook("existing_report.xlsx")
ws = wb["売上データ"]
# ── 特定セルを更新 ──────────────────────────────────────
ws["B2"] = 999999 # 値を上書き
ws["C2"].value = "更新済み" # .value で更新
# ── 最終行の次に行を追加 ───────────────────────────────
new_row = ["新規データ", 100, 50000]
ws.append(new_row)
# ── 条件で値を一括更新 ────────────────────────────────
for row in ws.iter_rows(min_row=2, values_only=False):
name_cell = row[0]
sales_cell = row[2]
if sales_cell.value and sales_cell.value > 600000:
# 売上 60 万超の行に色を付ける
from openpyxl.styles import PatternFill
gold_fill = PatternFill(fill_type="solid",
start_color="FFF2CC", end_color="FFF2CC")
for cell in row:
cell.fill = gold_fill
# ── バックアップを取ってから保存 ─────────────────────
import shutil
src = Path("existing_report.xlsx")
shutil.copy(src, src.with_suffix(".bak.xlsx")) # バックアップ
wb.save("existing_report.xlsx") # 元のファイルを上書き
# または別名で保存
wb.save("existing_report_updated.xlsx")
wb.close()
バックアップのすすめ: 既存ファイルを上書きする前に
shutil.copy() でバックアップを取る習慣をつけましょう。特に本番データを扱う場合は、save() 前にコピーを作成してから実行します。
12. pandas で書き出す
DataFrame を to_excel() で Excel に出力します。複数シートへの出力や書式設定も可能です。
import pandas as pd
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment
# ── サンプルデータ ───────────────────────────────────────
df = pd.DataFrame({
"名前": ["田中", "鈴木", "佐藤"],
"年齢": [30, 25, 35],
"売上": [520000, 380000, 670000],
"登録日": pd.to_datetime(["2026-01-15", "2026-02-10", "2026-03-05"]),
})
# ── 基本 ─────────────────────────────────────────────────
df.to_excel("output.xlsx", index=False)
# ── 主要オプション ────────────────────────────────────────
df.to_excel(
"output.xlsx",
sheet_name="売上データ", # シート名
index=False, # インデックスを書き出さない
startrow=1, # 書き出し開始行(0-indexed)
startcol=0, # 書き出し開始列
engine="openpyxl", # エンジン指定
)
# ── 複数シートに書き出す(ExcelWriter) ─────────────────
df_summary = pd.DataFrame({"月": ["1月","2月","3月"], "合計": [520000, 380000, 670000]})
with pd.ExcelWriter("multi_sheet.xlsx", engine="openpyxl") as writer:
df.to_excel(writer, sheet_name="詳細", index=False)
df_summary.to_excel(writer, sheet_name="サマリー", index=False)
# ── ExcelWriter + openpyxl で書式設定 ──────────────────
with pd.ExcelWriter("styled.xlsx", engine="openpyxl") as writer:
df.to_excel(writer, sheet_name="売上", index=False)
# writer.book から Workbook オブジェクトを取得して追加編集
wb = writer.book
ws = writer.sheets["売上"]
# ヘッダー行に背景色と太字を設定
header_fill = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
for cell in ws[1]: # 1行目(ヘッダー)
cell.fill = header_fill
cell.font = Font(bold=True, color="FFFFFF")
cell.alignment = Alignment(horizontal="center")
# 売上列(C列)に通貨フォーマット
for row in ws.iter_rows(min_row=2, min_col=3, max_col=3):
for cell in row:
cell.number_format = "¥#,##0"
ExcelWriter のコンテキストマネージャ:
with pd.ExcelWriter(...) as writer: のブロックを抜けると自動的に save() が呼ばれます。ブロック内で openpyxl オブジェクトを使った追加編集も反映されます。
13. 実践スクリプト
CSV から読み込んだデータを整形して、書式付き Excel レポートを生成するスクリプトです。openpyxl と pandas を組み合わせた実践パターンです。
"""
Excel 書き込み実践スクリプト
- CSV を読み込んで整形し、書式付き Excel レポートを生成
"""
import openpyxl
import pandas as pd
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
from pathlib import Path
# ── スタイル定数 ─────────────────────────────────────────
HEADER_BG = PatternFill(fill_type="solid", start_color="1F3864", end_color="1F3864")
HEADER_FONT = Font(bold=True, color="FFFFFF", size=11)
EVEN_BG = PatternFill(fill_type="solid", start_color="D9E1F2", end_color="D9E1F2")
TOTAL_FONT = Font(bold=True, size=11)
THIN = Side(border_style="thin", color="CCCCCC")
ALL_BORDER = Border(left=THIN, right=THIN, top=THIN, bottom=THIN)
CENTER = Alignment(horizontal="center", vertical="center")
RIGHT = Alignment(horizontal="right", vertical="center")
def apply_header_style(ws, row=1):
"""1行目にヘッダースタイルを適用"""
for cell in ws[row]:
cell.fill = HEADER_BG
cell.font = HEADER_FONT
cell.alignment = CENTER
cell.border = ALL_BORDER
def apply_data_style(ws, start_row=2):
"""データ行に交互色・罫線・配置を適用"""
for r_idx, row in enumerate(ws.iter_rows(min_row=start_row), start=start_row):
for cell in row:
cell.border = ALL_BORDER
cell.alignment = RIGHT if isinstance(cell.value, (int, float)) else CENTER
if r_idx % 2 == 0:
cell.fill = EVEN_BG
def set_column_widths(ws, widths: dict):
"""列幅を設定 {'A': 20, 'B': 10, ...}"""
for col_letter, width in widths.items():
ws.column_dimensions[col_letter].width = width
def generate_sales_report(input_csv: str, output_xlsx: str):
"""CSV → 書式付き Excel レポートを生成"""
# CSV 読み込み
df = pd.read_csv(input_csv, encoding="utf-8-sig")
print(f"読み込み: {len(df)}件")
# 集計(pandas で)
summary = df.groupby("部署").agg(
件数=("売上", "count"),
合計売上=("売上", "sum"),
平均売上=("売上", "mean"),
).reset_index()
# ExcelWriter で書き出し
with pd.ExcelWriter(output_xlsx, engine="openpyxl") as writer:
# 詳細シート
df.to_excel(writer, sheet_name="詳細", index=False)
# 集計シート
summary.to_excel(writer, sheet_name="部署別集計", index=False)
# openpyxl で書式を適用
wb = openpyxl.load_workbook(output_xlsx)
for sheet_name in ["詳細", "部署別集計"]:
ws = wb[sheet_name]
ws.freeze_panes = "A2" # ヘッダー行を固定
apply_header_style(ws)
apply_data_style(ws)
# 売上列に通貨フォーマット
for row in ws.iter_rows(min_row=2):
for cell in row:
if cell.column_letter in ("C", "D") and isinstance(cell.value, (int, float)):
cell.number_format = "¥#,##0"
# 詳細シートの列幅調整
set_column_widths(wb["詳細"], {"A": 16, "B": 10, "C": 14, "D": 12})
set_column_widths(wb["部署別集計"], {"A": 14, "B": 8, "C": 14, "D": 14})
wb.save(output_xlsx)
wb.close()
print(f"Excel を保存: {output_xlsx}")
if __name__ == "__main__":
generate_sales_report("sales.csv", "sales_report.xlsx")
生成される Excel のイメージ
【詳細シート】
| 名前 | 部署 | 売上 | 登録日 |
|----------|------|-----------|------------|
| 田中太郎 | 営業 | ¥520,000 | 2026/01/15 |
| 鈴木花子 | 開発 | ¥380,000 | 2026/02/10 |
...(交互色・罫線・ヘッダー固定)
【部署別集計シート】
| 部署 | 件数 | 合計売上 | 平均売上 |
|------|------|-------------|-------------|
| 営業 | 12 | ¥6,240,000 | ¥520,000 |
| 開発 | 8 | ¥3,040,000 | ¥380,000 |