プログラミング / 言語 / Python

Excel 書き込み
— openpyxl / pandas 完全ガイド

1. 新規ブックを作成する

openpyxl で新しい Excel ファイルを作成するには Workbook() でブックを生成し、save() で保存します。

python
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) で行います。リストや辞書を使った一括書き込みも便利です。

python
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 でフォントの種類・サイズ・太字・色などを設定できます。

python
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 でセルの背景色を設定します。ヘッダー行の色分けや、条件に応じた行のハイライトに使います。

python
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(単色塗りつぶし)が最もよく使われます。他に darkGridlightGriddarkTrellis などのパターンも指定できます。

5. 罫線(Border)を設定する

BorderSide を組み合わせて、上下左右の罫線を設定します。

python
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 で文字の水平・垂直配置、折り返し、インデントを制御します。

python
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
python
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)" の形式で書き込みます。

python
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. 行・列の操作

列幅・行高さの設定、行・列の挿入・削除、非表示設定を解説します。

python
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() で解除します。

python
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() で読み込み、保存します。

python
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 に出力します。複数シートへの出力や書式設定も可能です。

python
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 を組み合わせた実践パターンです。

python
"""
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 |