Pythonでopenpyxlを使ってExcelファイルをCSVに変換する方法
プログラマーの尾関です。
ゲーム開発の現場では、Excelファイル(.xlsx)をCSVファイルに変換する作業がよく発生します。手作業では手間がかかりますが、Pythonのopenpyxlライブラリを使えば自動化が簡単にできます。
この記事では、openpyxlのインストールから、サンプルデータの作成、そしてExcelからCSVへの変換方法までを順を追って解説します。
目次
openpyxlのインストール
Pythonの標準機能では Excel を読み込むのは難しいので、openpyxlをインストールします。
ターミナル(コマンドプロンプト)で以下のコマンドを実行します。
pip install openpyxl

正常にインストールできると「Successfully installed openpyxl-x.x.x」というメッセージが出力されます。
インストールが完了したら、Pythonでimport openpyxl
がエラーなく動作することを確認しましょう。
# この記述だけして実行できるかどうかを確認.
import openpyxl
print("Hello openpyxl!")
テストデータ (Excel) の作成
Excelでテストデータを作成します。
ここでは以下のようなデータとしました。

- ファイル名: Sample.xlsx
- シート名: Enemy
openpyxlでExcelを読み込む
openpyxlを使ってExcelを読み込むには「openpyxl.load_workbook()」を使用します。これはワークブック (Excelファイル全体) を開く処理となります。
ここで欲しいのは “Enemy” シートなので、ブックからシート情報を取り出します。今回は入力されている行すべてを取り出したいので、iter_rows() を使うと便利です。
上記のことをコードにすると以下の通りとなります。
# coding: utf_8
import openpyxl
def main():
# Sample.xlsxを読み込む.
wb = openpyxl.load_workbook('Sample.xlsx')
# シートを取得する.
sheet = wb["Enemy"]
s = "" # 出力テキスト.
for line in sheet.iter_rows(values_only=True):
for idx, d in enumerate(line):
if idx == 0:
s += str(d)
else:
s += "," + str(d) # カンマ区切りにする.
s += "\n"
# 確認用.
print(s)
# 出力するCSVファイル.
f = open("Sample.csv", "w")
f.write(s)
f.close()
if __name__ == "__main__":
main()
もし指定のセルのみ取得したい場合は、cell([row], [col]).value で値を取得することができます。
# coding: utf_8
import openpyxl
def main():
# Sample.xlsxを読み込む.
wb = openpyxl.load_workbook('Sample.xlsx')
# シートを取得する.
sheet = wb["Enemy"]
print(sheet.cell(2, 3).value) # 2行目,3列目の値を取得.
if __name__ == "__main__":
main()
おしまい
openpyxlを使えば、Excelファイルの読み書きやCSVへの変換が簡単に自動化できます。
以上、ゲーム開発の効率化のお役に立てれば幸いです。
おまけ: UEデータテーブル用 CSV変換ツール
Unreal Engineでデータテーブルにインポートするために、ExcelをCSVに変換するツールを作成したので添付しておきます。
このPytyonスクリプトは CMDシートに定義されているリストから対象のシートをCSVファイルとして出力するツールです。

例えばこのようなデータがあります。

これをコンバートすると以下のCSVに変換されます。

Pythonスクリプト全文
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ====================================================================
# ExcelからCSVにするツール
# --------------------------------------------------------------------
# @note 実行には openpyxl のインストールが必要
# >pip install openpyxl
# ====================================================================
import sys
import csv
from tkinter import messagebox
from openpyxl import load_workbook
import traceback
from openpyxl.workbook.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet
from typing import List
# 定数定義.
# ■コマンドシート関連.
# コマンドシート名.
CMD_SHEET_NAME = "CMD"
# コマンドシートの情報.
# 1列目はコンバート対象のシート名 (openpyxlのvalues_only=Trueは0始まり).
CMD_COL_SHEET = 0
# 2列目は出力するCSVファイル名.
CMD_COL_CSV = 1
# ■各データシート関連.
# 1行目は有効無効を判定するヘッダ行.
DATA_ROW_HEADER = 1
# 2行目は列名を保持する行.
DATA_ROW_COLUMN_NAME = 2
# 有効無効の文字列表記.
DATA_VALID_TRUE = '<1>'
DATA_VALID_FALSE = '<0>'
# コマンド情報を格納するクラス.
class CmdInfo:
def __init__(self, sheet_name: str, csv_name: str):
# コンストラクタ.
self.sheet_name = sheet_name
self.csv_name = csv_name
# csv_nameの拡張子が.csvでなければエラーとする.
if not self.csv_name.endswith('.csv'):
raise ValueError(f"CSVファイル名は '.csv' で終わる必要があります: {self.csv_name}")
def is_valid(self) -> bool:
# csv_nameの拡張子が.csvでなければ無効.
# どちらかが空文字だったら無効とする.
return bool(self.sheet_name and self.csv_name.endswith('.csv'))
def __repr__(self):
# 表示用文字列.
return f"CmdInfo: {self.sheet_name} -> {self.csv_name}"
# CMDシートをパースしてコマンドのリストを返します.
def parse_cmd_sheet(ws_cmd: Worksheet) -> List[CmdInfo]:
# 1列目が有効な行をCMD情報としてパースします.
cmds: List[CmdInfo] = []
for row in ws_cmd.iter_rows(values_only=True):
if row[CMD_COL_SHEET] and row[CMD_COL_CSV]:
cmd_info = CmdInfo(sheet_name=str(row[CMD_COL_SHEET]), csv_name=str(row[CMD_COL_CSV]))
if cmd_info.is_valid():
cmds.append(cmd_info)
return cmds
# データのヘッダ情報を格納するクラス.
class DataHeader:
def __init__(self):
# 有効な列と無効な列をリストとして保持する.
self.valid_columns :List[bool] = []
# 行名を保持する.
self.column_names :List[str] = []
# データ開始行.
self.start_row = 0
def append_valid_column(self, b :bool):
# 有効な列を追加します.
self.valid_columns.append(b)
def append_column_name(self, name: str):
# 列名を追加します.
self.column_names.append(name)
def get_first_valid_column_index(self) -> int:
# 有効な列の先頭の列番号を取得します.
for idx, valid in enumerate(self.valid_columns):
if valid:
return idx
return 0
def parse_header(self, ws: Worksheet):
# ヘッダ行を解析して有効な列と列名を設定します.
for idx, row in enumerate(ws.iter_rows(min_row=DATA_ROW_HEADER, max_row=DATA_ROW_COLUMN_NAME, values_only=True), start=DATA_ROW_HEADER):
if idx == DATA_ROW_HEADER:
# 1行目は有効無効を判定するヘッダ行.
for col in row:
if str(col) == DATA_VALID_TRUE:
# 有効な列.
self.append_valid_column(True)
else:
# 無効な列.
self.append_valid_column(False)
elif idx == DATA_ROW_COLUMN_NAME:
# 2行目は列名を保持する行.
for col, valid in zip(row, self.valid_columns):
if valid:
self.append_column_name(str(col))
else:
self.append_column_name('')
# データ開始行を設定.
self.start_row = DATA_ROW_COLUMN_NAME + 1
def __repr__(self):
# 表示用文字列.
return f"有効列={self.valid_columns},\nカラム名={self.column_names})"
# 指定されたシートをパースしてCSVに変換します.
def convert_sheet_to_csv(wb: Workbook, cmd: CmdInfo):
# シート名が存在するか確認.
if cmd.sheet_name not in wb.sheetnames:
raise ValueError(f"'{cmd.sheet_name}' シートが見つかりません。")
# シートを取得.
ws: Worksheet = wb[cmd.sheet_name]
# ヘッダの解析.
header: DataHeader = DataHeader()
header.parse_header(ws)
print(f"ヘッダ情報: {header}")
# ヘッダー行の作成.
headers: List[str] = []
for valid, name in zip(header.valid_columns, header.column_names):
if valid:
# 有効な列のみを出力.
headers.append(name)
# データ本体の抽出.
get_first_valid_column_index: int = header.get_first_valid_column_index() # 有効な列の先頭の列番号を取得.
data_rows: List[List[str]] = []
for row in ws.iter_rows(min_row=header.start_row, values_only=True):
# コメント行かどうかを確認.
if row[0] is not None and str(row[0]).startswith('#'):
# 0列目がコメント行の場合はスキップ.
continue
# データ行かどうかを確認.
if row[get_first_valid_column_index] is None:
continue # 空行はスキップ
# 有効な行かどうかを確認 (#で始まる行は無効).
row_0 = str(row[get_first_valid_column_index])
if (len(row_0) <= 0 or row_0.startswith('#')):
continue # 無効な行はスキップ
# データ行を出力用に整形.
row_data: List[str] = []
for idx, valid in enumerate(header.valid_columns):
if valid:
value = row[idx]
if value is None:
value = ''
else:
value = str(value).strip()
row_data.append(value)
print(row_data)
data_rows.append(row_data)
# CSVに書き出し
with open(cmd.csv_name, 'w', newline='', encoding='utf-8-sig') as csvfile:
writer = csv.writer(csvfile, quoting=csv.QUOTE_MINIMAL)
writer.writerow(headers) # ヘッダー行
writer.writerows(data_rows)
print(f"変換完了: {cmd.csv_name}")
def main():
# 起動引数からExcelファイル名を取得.
if len(sys.argv) > 1:
input_excel = sys.argv[1]
else:
# エラー
raise ValueError("Excelファイル名を指定してください。例: python excel_to_csv.py Sample.xlsx")
# Excelファイルの読み込み
wb: Workbook = load_workbook(input_excel, data_only=True)
# CMDシートからコンバート情報を取得します.
# CMDシートが存在しない場合はエラーになります。
if CMD_SHEET_NAME not in wb.sheetnames:
raise FileNotFoundError(f"'{input_excel}' に '%s' シートが見つかりません。"%CMD_SHEET_NAME)
ws_cmd: Worksheet = wb[CMD_SHEET_NAME]
# コマンドシートをパースしてコマンド情報を取得します.
cmds: List[CmdInfo] = parse_cmd_sheet(ws_cmd)
if not cmds:
raise ValueError(f"'{CMD_SHEET_NAME}' シートに有効なコマンドが見つかりません。")
print("コマンド情報:")
for cmd in cmds:
print(" - ", cmd)
# 各コマンドに対して処理を行います.
for cmd in cmds:
print(f"シート名: {cmd.sheet_name}, CSVファイル名: {cmd.csv_name}")
convert_sheet_to_csv(wb, cmd)
if __name__ == "__main__":
try:
main()
except Exception as e:
# エラー発生.
print(traceback.format_exc())
messagebox.showerror("エラー発生", traceback.format_exc())