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)形式」として読み込まれるようになりました

例えば、以下のように年月を「ユーザー定義」でyyyy"年"m"月"としていたのに、OpenPyXLでは「標準」で読み込まれるので、日付で入力していた値はシリアル値で取得されます。

xl custom format

# ユーザー定義のはずが「標準(General)」で読み込まれる
>>> ws["B1"].number_format
'General' 

# 値はシリアル値で取得される
>>> ws["B1"].value
43313 

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

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()では、ユーザー定義の書式形式はそのままコピーされます。

新しいバージョンでユーザー定義のセルを読み込む対処例

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

対処法1:手動でエクセルファイルを修正する

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

xl date format

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

>>> ws["B1"].value
datetime.datetime(2018, 8, 1, 0, 0)
>>> ws["B1"].number_format
'yyyy"年"m"月";@'

対処法2:コードで値を変換する

コードで日付のシリアル値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ドキュメント)