Pythonによるオフィスワークの自動化では、エクセルファイルを操作できるopenpyxlは欠かせないライブラリです。インストールは以下のようにpipを用いて簡単にできます。

C:¥Users¥ichiro> py -m pip install openpyxl
# その他の例(環境によりコマンドは様々です)
mac:~ ichiro$ python3 -m pip install openpyxl

openpyxlを用いて既存のエクセルファイルを読み込むには、以下のようにload_workbook()を用います。

>>> import openpyxl
>>> wb = openpyxl.load_workbook("./Files/tokyo_branch_201808.xlsx")
>>> ws = wb["Sheet1"]
>>> ws["A1"].value
'東京本社月次決算'

読み込むファイルに、表示形式が「ユーザー定義」に設定されているセルを含んでいると、以前は例外としてエラー(KeyError)が表示されたのですが、バージョン2.6.1からそのセルは標準(General)形式」として読み込まれるようになりました

つまり、以下のように想定と違う表示形式で読み込まれることになるので注意が必要です。

>>> ws["B1"].number_format
'General' 
# ユーザー定義のはずが「標準(General)」で読み込まれる
>>> ws["B1"].value
43313 
# 値はシリアル値で取得される

xl custom format

load_workbook()は真っ先によく用いるメソッドですので、最初でつまずかないよう少し詳しく解説してみました。参考にしていただけたら嬉しいです。

以前は例外として扱われた

以前(バージョン2.6.0まで)は、以下のように読み込むエクセルファイル内に認識できない形式のセルがあるとエラーKeyError)が表示されていました。

>>> wb = openpyxl.load_workbook("./Files/tokyo_branch_201808.xlsx")Traceback (most recent call last):
  File "", line 1, in 
    wb = openpyxl.load_workbook("./Files/tokyo_branch_201808.xlsx")
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\reader\excel.py", line 312, in load_workbook
    reader.read()
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\reader\excel.py", line 273, in read
    apply_stylesheet(self.archive, self.wb)
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\styles\stylesheet.py", line 189, in apply_stylesheet
    stylesheet = Stylesheet.from_tree(node)
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\styles\stylesheet.py", line 103, in from_tree
    return super(Stylesheet, cls).from_tree(node)
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\descriptors\serialisable.py", line 104, in from_tree
    return cls(**attrib)
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\styles\stylesheet.py", line 93, in __init__
    self._normalise_numbers()
  File "C:\Users\ichiro\AppData\Local\Programs\Python\Python37-32\lib\site-packages\openpyxl\styles\stylesheet.py", line 166, in _normalise_numbers
    fmt = BUILTIN_FORMATS[style.numFmtId]
KeyError: 55

上記のエラーは、以下のopenpyxl.styles.numbersモジュールのソースコードを見ると分かりますが、BUILTIN_FORMATSの辞書には55というキーがないことを表しています。

openpyxl.styles.numbersモジュールのソースコード

このように、以前は認識できない表示形式のセルがあると例外として教えてくれましたが、バージョン2.6.1で以下のIssue(課題)の投稿に対処し、認識できない形式は「標準(General)」として読み込むようになりました。

Issue#1182 ‘I cannot load my excel file though openpyxl’

シートのコピーは問題ありません

なお、copy_worksheet()では、ユーザー定義の書式形式はそのままコピーされます。

ユーザー定義の形式のセルがある場合どうするか?

上記の例では、セルの値が日付のはずがシリアル値として読み込まれてしまうので何か対策が必要です。該当するファイルが少なければ、手動でファイルを修正するのが簡単ですが、数量が多くなる場合はコードで実装しておく必要があります。

手動でエクセルファイルを修正する場合

以下のようにセルの書式設定で「表示形式」を組み込みの日付に変更します。

xl date format

コードからこの修正したファイルを読み込むと、以下のように日時(datetime.datetime)で認識されているのが確認できます。表示形式もエクセルで設定した通りになっています。

>>> ws["B1"].value
datetime.datetime(2018, 8, 1, 0, 0)
>>> ws["B1"].number_format
'yyyy"年"m"月";@'
コードで値を変換する場合

コードで日付のシリアル値datetime.datetime型に変換するには、openpyxl.utils.datetimeモジュールfrom_excel()関数を用います。

>>> dt = ws["B1"].value
>>> dt
43313
>>> from openpyxl import utils
>>> dt = utils.datetime.from_excel(dt)
>>> dt
datetime.datetime(2018, 8, 1, 0, 0)

openpyxl.utilsパッケージには、結構便利なツールがありますので、一度のぞいて見ることをオススメします。

openpyxl.utilsパッケージ(openpyxlドキュメント)