PythonでDBデータをExcel出力するには?〜pandasの基本とハマりどころ〜
はじめに
本記事は、pandasを使ってDBデータをExcelに出力する方法を整理したものです。
「とりあえず動くコードが欲しい」方から「実務で安全に使いたい」方まで参考になるようにまとめました。
DBデータを安全にExcel出力できるようになり、実務で遭遇しがちなエラーの回避方法も把握できます。
本記事では以下を扱います。
- pandasでDBデータをExcelに出力する基本の流れ
-
engineに指定できる値の種類 - 他ライブラリとの使い分け
- おまけ:制御文字エラー(IllegalCharacterError)の落とし穴と回避策
- その他のハマりやすいエラーと対処法
pandas公式ドキュメント:pandas documentation
DBデータを取得してExcelに出力する基本
DBからデータを読み込む
今回はSQLAlchemyを経由してDBからDataFrameに読み込みます。
import os
import pandas as pd
from sqlalchemy import create_engine
# DB接続(例: PostgreSQL)
# 接続情報は環境変数から取得(直書きはしない)
engine = create_engine(os.environ["DATABASE_URL"])
# SQLからDataFrameに読み込み
df = pd.read_sql("SELECT * FROM users", engine)
Excelに出力する
DataFrame.to_excel() を呼ぶだけで簡単にExcelファイルを作成できます。
df.to_excel("users.xlsx", index=False)
ここで指定している index=False は、DataFrameのインデックス(通常は行番号)を出力しない という意味です。
デフォルト(index=True)の場合は、左端にインデックス列が追加されます。
出力イメージ
index=True(デフォルト)
| id | name | |
|---|---|---|
| 0 | 1 | Alice |
| 1 | 2 | Bob |
| 2 | 3 | Charlie |
index=False
| id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
👉 単純にDBのレコードをExcelに出したい場合は index=False を指定します。
engine に指定できる値の種類
pandasの to_excel は内部で外部ライブラリ(engine)を使ってExcelファイルを扱います。
| engine | 説明 | 適したケース |
|---|---|---|
| openpyxl(デフォルト) | .xlsx 標準の読み書きライブラリ | 読み書き両対応。Excel仕様に忠実で厳格。標準利用のため。 |
| xlsxwriter | 書き込み専用の高速ライブラリ(インストール済みなら) | 書式設定や制御文字回避が必要なとき。 |
| xlwt | .xls(古いExcel 97–2003形式)専用 | 互換性維持が必要な旧環境のみ |
| odf | OpenDocument形式(.ods)用 | LibreOffice系をターゲットにする場合 |
エンジンは別途インストール:openpyxl / xlsxwriter / odfpy は optional dependency。
👉 まずは openpyxl。制御文字や書式にこだわるなら xlsxwriter。LibreOfficeなら odf。
Excel出力時の主なエラーと対処法
制御文字エラー
今回pandasを扱うにあたって、DBの文字列に制御文字が含まれていたためエラーが発生する事象にも遭遇しました。
DBの値を出力するという要件のため、DB自体に入りうる値については十分な考慮を行ったうえで実装する必要があります。
再現コード
import pandas as pd
# \v は垂直タブ(禁止される制御文字)
df = pd.DataFrame({"name": ["Alice", "Bob\vSmith"]})
df.to_excel("out.xlsx") # openpyxl 使用時に例外
実行時に以下のエラーが発生します(pandas → openpyxl 実行時のエラー)。
IllegalCharacterError: control character ... found in string
原因
-
openpyxlは Excel(Open XML)仕様に準拠しており、制御文字のうち\x00–\x08,\x0B–\x0C,\x0E–\x1Fが禁止です。-
\t(タブ)、\n(改行)、\r(復帰)は許容範囲ですが、\v(垂直タブ)や\x00(NULL)などは不可のため、セルに含まれていると IllegalCharacterError が発生します。
参考:StackOverflow: What are all the illegal characters from openpyxl?
-
解決策1:engineを変える
engine="xlsxwriter" を指定すると、本件の禁止制御文字が原因の例外を回避できるケースがあります。
👉ただし、既存Excelへの追記は不可。要注意。
df.to_excel("out.xlsx", engine="xlsxwriter")
今回の要件範囲では副作用は確認できませんでしたが、xlsxwriter は書き込み専用で、openpyxl と一部の書式・数式挙動が異なる点があります。
解決策2:前処理で除去
既存テンプレートや厳密な書式要件がある場合は事前検証をおすすめします。
前処理で禁止制御文字を除去/置換しておくと堅牢です。
# 禁止制御文字(\x00–\x08, \x0B–\x0C, \x0E–\x1F)をスペースに置換
df = df.applymap(
lambda s: (
s if not isinstance(s, str)
else re.sub(r'[\x00-\x08\x0B-\x0C\x0E-\x1F]', ' ', s)
)
)
tip: 前処理で置換する場合は、Pythonファイルの先頭に import re を忘れず。
その他のハマりやすいエラー
他にもpandasの利用にあたり発生しうるエラーとその解決策をまとめました。
| エラー | 原因 | 解決策 |
|---|---|---|
| PermissionError / OSError | 出力先ファイルがExcelで開かれている / 権限不足 |
with pd.ExcelWriter(...) を使う / ファイルを閉じる |
| ValueError(シート名) | シート名が31文字超 / 禁止文字 : \ / ? * [ ] を含む |
シート名を短く、安全に命名 |
| TypeError | タイムゾーン付きdatetime / dict / list をセルに含む |
tz_localize(None) でtzを削除 / 型変換してから出力 |
| OverflowError / MemoryError | Excel仕様の制限超過(行: 1,048,576 / 列: 16,384)や巨大データ | 出力前に制限確認 / CSVやParquetなど別形式を検討 |
他ライブラリとの関係(pandas vs Polarsなど)
最後にpandas以外のデータフレームライブラリとの比較を添えます。
Excel出力に関してはpandasが最も充実しています。
| ライブラリ | 強み | Excel出力の扱い |
|---|---|---|
| pandas | 標準、資料が豊富 |
to_excel が充実 |
| Polars | Rust実装で高速・低メモリ | Excel出力は弱く、pandas変換が必要 |
| Modin | pandas互換で並列化 | 出力はpandas依存 |
| Dask DataFrame | 並列・分散処理 | 出力はpandasへ寄せる |
| PySpark | ビッグデータ処理 | ローカルに集約してpandasで出力 |
👉 処理はPolars/Dask/Sparkで行っても、最後のExcel出力はpandasに戻すのが現実的です。
まとめ
- pandasでDBデータをExcel出力する基本は
to_excel一発 indexの扱いに注意(デフォルトは行番号も一緒に出力される)engineの種類を理解するとトラブル対応や応用が効く- Polars/Dask/Sparkでも処理はできるが、最終的にExcel出力はpandas
-
制御文字エラーはハマりやすい落とし穴 →
xlsxwriterで回避が可能 -
その他のエラー(ファイル書き込み、シート名制限、データ型、Excel仕様上限)も考慮してライブラリを利用する
👉 本記事を読めば、DBデータを安全にExcel出力し、pandas利用時に発生する可能性のあるエラーを事前に回避できるようになります。
参考
今回もpandasを理解するにあたりサプーさんの動画にとても助けられました。
Discussion