こめきちdiary

日々の仕事、生活でぶち当たった問題の解決策を書き残しています。あなたのお役に立てれば幸いです。

【Python】openpyxlでセルの文字列の色が変更される事象の回避方法について

はじめに

openpyxlは、phthonからExcelファイルを読み書きするためのライブラリです。非常に使いやすくて、私も重宝しています。
難点があるとすれば、1セルの中で色分けした文字列が保存時に単色になってしまうことです。
openpyxlでは1セル内に複数の書式が混在する場合、1文字目の書式が文字列に適用されます。
この問題の解決法を記しておきます。

結論から言うと、セルの書式が欠損するのはopenpyxlの制限なので、openpyxlを使用する限りは回避できません。
ではどうするか。
openpyxlの代わりにwin32comを使用して、pythonからExcelを起動して直接操作する方法を採ります。
ですので、ここに書いた解消手段は、エクセルがインストールされたWindows環境が必要となりますのでご注意下さい。

なお、お急ぎの方は途中をすっ飛ばして対処方法②win32comで操作するをご参照ください。

目次


1.事象の再現

まずは、サンプルプログラムで事象を再現してみます。
下のようなエクセルに文字列を埋め込むような場合を想定します。ファイル名は「エクセルファイルテンプレート.xlsx」とします。
納品書のテンプレートから、発注ごとに納品書を生成するイメージです。

サンプルファイル

このエクセルファイルに「注文番号」と「品名」を埋め込むpythonコードは以下のようになるでしょう。

# サンプルプログラム xlsxedit_openpyxl.py
import shutil
import openpyxl
#ファイルをコピーする
filename = "エクセルファイル結果.xlsx"
shutil.copy('エクセルファイルテンプレート.xlsx', filename)
#ワークブックを開く
wb = openpyxl.load_workbook(filename)
sheet = wb.active
#セルに値を書き込む
sheet.cell(row=1, column=2).value = "A12345"
sheet.cell(row=2, column=2).value = "あいうえお"
#保存して抜ける
wb.save(filename)
wb.close

処理の内容は、テンプレートからコピー(エクセルファイル結果.xlsx)したファイルをopenpyxlで開き、編集(文字列埋め込み)、保存しています。

C:\test>python xlsxedit_openpyxl.py

C:\test>

実行結果は以下のようになります。
1セル中の文字色が単一色に変換されてしまいます。

openpyxlを使用しての更新


2.対処方法

冒頭に記したようにopenpyxlでは回避できません。
対処法には、消極的対処(①)と積極的対処(②)があります。

①1セル1色しか使わない

あたりまえのことですが、テンプレートを作成する際にこのルールで作れば問題は発生しません。
複数色をつける必要がある場合は、セルを分割して文字ごとにセルにいれれば見た目のイメージはそのままに対処が可能です。
でもそれがやりたくないから対処方法を調べているわけで。。。

②win32comで操作する

openpyxlの代わりに、win32comを使用します。
win32comとはWindows上で出来る操作をPythonから実行できるライブラリです。

win32comを使用してエクセルファイルに「注文番号」と「品名」を埋め込むpythonコードは以下のようになります。

# サンプルプログラム xlsxedit_win32.py
import shutil
#import openpyxl
import win32com.client
from pathlib import Path
#ファイルをコピーする
filename = "エクセルファイル結果2.xlsx"
shutil.copy('エクセルファイルテンプレート.xlsx', filename)
#エクセルを起動する
excel = win32com.client.Dispatch('Excel.Application')
excel.Visible = True
# 絶対パスで開く必要があるのでカレントディレクトリ位置を取得
nowdir = Path(__file__).absolute().parent
filename = str(nowdir) + "\\" + filename
#ワークブックを開く
wb = excel.Workbooks.Open(filename)
sheet = wb.sheets(1)
#セルに値を書き込む
sheet.cells(1, 2).value = "A12345"
sheet.cells(2, 2).value = "あいうえお"
#保存してエクセルを終了する
wb.Close(True)
excel.Quit()

上記のプログラムを実行します。

C:\test>python xlsxedit_win32.py

C:\test>

実行結果は以下のようになります。
意図した通り、もとの書式を損なわずに更新できています。

win32comを使用して更新


3.最後に

win32comはエクセルを直接操作しますので、Excelアプリで出来ることは何でも再現することが可能になります。
他のライブラリで行き詰まった場合の最終手段として憶えておくと良いと思います。
この方式にはメリットがもう一つあって、openpyxlに比べて実行速度が格段に早いことです。
openpyxlの処理速度の遅さに悩んでいる方にも有効な解決手段となり得ます。



以下のサイトに大変お世話になりました。ありがとうございます。
ExcelPythonの最強コラボ!】win32comを使ったExcel操作のすべて
https://www.cfxlog.com/python_win32com_excel/
PythonからExcelをwin32comで操作する
https://qiita.com/kumarstack55/items/9ae3432446afca06497f

以上です。