はじめに
Excel ファイルを自動で処理したい。そのための言語としていくつか手段があることが分かった。
どれを選ぶべきか。その中で結果として選んだ Python + win32com の操作例を書き残しておく。
なぜ Python + win32com で Excel を操作するのか
Excel VBA, PowerShell + COM, Python + xlwings, Python + openpyxl, Python + win32com を比較した結果、自分のニーズに近いのは win32com でした。
まず、Excelを自動で操作したいと考えたとき、初めに思いつくのはExcel VBAでした。ただ、これは向かない用途があるようでした。
Excelファイルの所有者にVBA入れて管理するにはすべてのユーザにマクロ付きのExcelファイルの利用を強制することになるのでOSからExcelのバージョンまで細かな動きをVBA側で考慮する必要があります。VBAを強制することによるセキュリティ上のリスク等も考え始めるとあまり選択肢としてよくない。
次に、データが詰まってるExcelファイルとVBAを管理するExcelファイルを分離することを考えたとしても、VBAのコード管理がめんどい。Gitなりで非テキストファイルを管理するのは簡単でなく、がんばってモジュールを節目でエクスポートして管理、というのは面倒そう。継続的にメンテする費用がなければ避けたい選択肢だった。
Excel VBA の次に Excel の自動化で思いついたのは、 PowerShell + COM でした。が、PowerShell って癖があって苦手意識がある。エスケープがバックスラッシュなの忘れるし、パス名にワイルドカード文字列が入るとエスケープ処理に翻弄される程度の、低レベル PowerShell コーダーには敷居が高すぎる。100行程度ならPowerShellで頑張る。それ以上はお勧めできない。
次に他の言語はさておき、 Python + win32com か、xlwings, openpyxl で悩んだ。
openpyxl は、xlsx を読めるが計算式の計算手段を持って無い気がするのに計算結果を得られそうなメソッドがあって不安がある。おそらくキャッシュしたデータを読めるのだと思うけれど値に不整合があるのではないか、仮に無かったとして書き込み処理を行った後に読んではいけないデータなのではと不安を感じる。誤差が許される読み取り処理や、読み取りせずExcelを出力するならopenpyxlは使い勝手よさそう。
xlwings は、素朴なExcelファイルなら採用したいくらい良くできてる。 dir(workbook)
でメソッド一覧が出る、メソッド名が原則小文字で統一されている、など使い勝手が良い。ただし、すべてのExcelの機能に対応してないように見える。例えば、ざっと調べた限りでは保護されたExcelシートを解除したい場合は、VBAのコードをxlwings経由で実行する等のワークアラウンドが必要でデータ処理には向くが、かゆいところに手が届かない印象。また、Execel を導入済が前提となる。動作確認してないが macOS で動かしている事例があった。
残ったwin32comは上記の課題をすべてクリアできそう。ただし、制約として、Windows+Excel導入済みが前提になる。デスクトップでExcelを実行中にwin32com経由でExcelを実行すると挙動が変わる部分があるなどテスト面で環境の影響を受けやすい。 dir(workbook)
でメソッド名の一覧が出なかったり、メソッド名の大文字小文字を誤ると期待通りに動かないといった動作もある。ドキュメントはあまりないので、Python書く際はVBAの知見なり、MS公式APIドキュメントなり、Stackoverflowなりを参照することになる。
いずれも一長一短ですね。Excelを最も細かく使えるのがwin32comで一択。開発者にやさしく今後期待できるのがxlwings。全部で1~2画面程度の短いコードならPowerShellでもまあいいか。Excelファイルを自分だけで使うなら版管理なしのVBAでもいいか、という印象です。
前提
- Windows 10
- Python3
- Excel 2013
ディレクトリ、ファイル
. (directory)
|
+ data (directory)
|
+-- sample.xlsx
パッケージをインストールする
pip install pywin32
Excel を読む
Excel を起動・終了する
import win32com.client
# 起動する
app = win32com.client.Dispatch("Excel.Application")
# 終了する
app.Quit()
xlsxファイルからワークブックを読み取り専用で開く
from pathlib import Path
# 開く
abspath = str(Path(r"data/sample.xlsx").resolve())
workbook = app.Workbooks.Open(abspath, UpdateLinks=0, ReadOnly=True)
# 閉じる
workbook.Close()
ワークブックの一覧を得る
xxx[Index]
と xxx(Index)
で要素番号の開始が異なるので注意する。
print(app.Workbooks.Count)
for i in range(0, app.Workbooks.Count):
print(app.Workbooks[i].name)
print(app.Workbooks.Count)
for i in range(1, app.Workbooks.Count + 1):
print(app.Workbooks(i).name)
ワークブックを有効にする
workbook.Activate()
シートの一覧を得る
print(workbook.Worksheets.Count)
for i in range(0, workbook.Worksheets.Count):
print(workbook.Worksheets[i].name)
print(workbook.Worksheets.Count)
for i in range(1, workbook.Worksheets.Count + 1):
print(workbook.Worksheets(i).name)
シート名からシートを得る
sheet = workbook.Worksheets("Sheet1")
シートからセルを得る
sheet.Cells.Item(1,1).Value
シートから範囲を得る
arg = "A1:B3"
r = sheet.Range(arg)
ret = list()
for row_index in range(1, r.Rows.Count + 1):
row = []
for col_index in range(1, r.Columns.Count + 1):
row.append(r(row_index, col_index).Address)
ret.append(row)
print(ret)
# --> [['$A$1', '$B$1'], ['$A$2', '$B$2'], ['$A$3', '$B$3']]
セルの計算式を得る
cell.Formula = '=1+2'
cell.Formula
# --> '=1+2'
cell.Value
# --> 3.0
セルの値を得る
row_index, col_index = 1, 2
cell = sheet.Cells(row_index, col_index)
cell.ClearFormats()
cell.NumberFormatLocal
# --> 'G/標準'
# 整数
cell.Value = 1
cell.Value
# --> 1.0
# 文字列
cell.Value = 'あ'
cell.Value
# --> 'あ'
# 日付に解釈されそうな文字列
cell.NumberFormatLocal
# --> 'G/標準'
cell.Value = '2019-01-01'
cell.NumberFormatLocal
# --> 'yyyy/m/d'
cell.Value
# --> pywintypes.datetime(2019, 1, 1, 0, 0, tzinfo=TimeZoneInfo('GMT Standard Time', True))
import datetime
datetime.datetime.fromtimestamp(timestamp=pywindt.timestamp(), tz=pywindt.tzinfo)
# --> datetime.datetime(2019, 1, 1, 0, 0, tzinfo=TimeZoneInfo('GMT Standard Time', True))
# 時刻に解釈されそうな文字列
cell.Value = '00:00:00'
cell.NumberFormatLocal
# --> 'h:mm:ss'
cell.Value
# --> '0.0'
cell.Value = '24:00:00'
cell.Value
# --> '1.0'
cell.Value = '48:00:00'
cell.Value
# --> '2.0'
セルのテキストを得る
cell.ClearFormats()
cell.NumberFormatLocal
# --> 'G/標準'
cell.Value = '=1'
cell.Value
# --> 1.0
cell.Text
# --> '1'
Excel を書く
新規ワークブックを開く
workbook = app.Workbooks.Add()
ワークブックを保存する
workbook.Save()
その他
Excel を見えなくする
app.Visible = False
シートの保護を設定・解除する
sheet.Protect()
sheet.Unprotect()
警告を表示しないようにする
app.DisplayAlerts = False
セルのアドレスを得る
cell.Address
# --> '$B$1'
cell.GetAddress()
# --> '$B$1'
cell.GetAddress(RowAbsolute=False, ColumnAbsolute=False)
# --> 'B1'
# NGな例
#cell.Address(RowAbsolute=False, ColumnAbsolute=False)
Tips
dirメソッドは使えない
import win32com.client
app = win32com.client.Dispatch("Excel.Application")
dir(app)
# --> ['_ApplyTypes_', '_FlagAsMethod', '_LazyAddAttr_', '_NewEnum', '_Release_', '__AttrToID__', '__LazyMap__', '__bool__', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_builtMethods_', '_enum_', '_find_dispatch_type_', '_get_good_object_', '_get_good_single_object_', '_lazydata_', '_make_method_', '_mapCachedItems_', '_oleobj_', '_olerepr_', '_print_details_', '_proc_', '_unicode_to_string_', '_username_', '_wrap_dispatch_']
参考資料
ライブラリ比較
PythonでExcelファイルを扱うライブラリの比較
https://note.nkmk.me/python-excel-library/
Microsoft
Workbook オブジェクト
https://docs.microsoft.com/ja-jp/office/vba/api/excel.workbook
Excel + VBA
VBAのソースを、Gitとかで管理する
https://qiita.com/ryotaro76/items/63cad3dfb891a9df7c88
PowerShell + Excel
PowerShell で Excel をどうのこうのすることに興味を持ってくれると嬉しい
https://qiita.com/miyamiya/items/161372111b68bad0744a
Python + openpyxl
openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files
https://openpyxl.readthedocs.io/en/stable/
Python + win32com
Python for Windows (pywin32) Extensions
https://github.com/mhammond/pywin32
TODO: Exccel + win32com でよくまとまった記事を見た記憶があるがリンクを見つけられない。見つけたら書く。
Python + xlwings
xlwings - Make Excel Fly!
https://docs.xlwings.org/en/stable/index.html
ExcelからPythonを使う
https://qiita.com/katzhide/items/60d0336b322105bf8fe9
PythonとxlwingsでExcelファイルをいじる
https://idontwannawork.github.io/posts/edit-excel-with-python-and-xlwings/
how to unprotect a sheet? #1032
https://github.com/xlwings/xlwings/issues/1032