古臭いVBAは使いたくない!代わりにイカしたPython使いたい!
でも…xlwingsは敷居が高そうで、いざ使うとなるとなかなか難しそう。
このように考えている方、かなり多いんじゃないでしょうか?
まず初めにお伝えしておくと、xlwingsは順を追って理解すればシンプルで使いやすく、実用的です。
このページでは、xlwingsを使ったことが無い方でも順を追って理解しやすいように、本質的な点も交えて解説しています。
対象読者
- ExcelマクロにVBAを使うのにはウンザリしている人
- Excelマクロを少しは自分で作ったことがある人(初心者レベルでOK)
- Pythonの初歩的な部分を理解している人
VBAからpythonへの乗り換えを強く推奨するためにこの解説ページを作りました。
分かりにくい所があれば、お気軽に質問コメントお書き下さい。
xlwingsとは
xlwingsを使うと、これまでVBAで記述していたExcelマクロをPythonで記述することができるようになります。
xlwingsはそのためのインターフェイスを提供してくれます。
BSDライセンスとして提供されているので、バリバリに営利目的の業務に使っても問題ありません。
xlwingsを使うことによるメリット:
- Excel自動化のためのVBA開発者確保は必要無くなる
- 開発コストが大幅に下がる(体感的に5倍の生産性)
- 開発者のストレスが激減しやる気が激増する
- 処理内容によっては速度の大幅向上も期待できる(pandasライブラリの利用など)
xlwingsをインストール
xlwingsはpipコマンドでインストールすることができます。
pip install xlwings xlrd openpyxl
ポイント
xlwingsをインストールする際に、依存関係のうちxlrdとopenpyxlは暗黙的にインストールされてくれないため、一緒にインストールしておきましょう。
インストールに関する公式の解説ページはこちら。
>> xlwings インストール
Pythonから開いてあるExcelを操作する
xlwingsをインストールしたら、PythonからExcelを操作することができます。
HelloXlwingsプロジェクトを作って、空のExcelファイルと、Pythonプログラムを作りましょう。
HelloXlwings
|- hello.xlsx
|- hello.py
まずはhello.xlsxを作ります。
これは本当にファイルを作るだけで大丈夫です。

ファイルを作ったら、開いた状態にしておきます。
(今回のサンプルは開いているExcelを操作するサンプルです)
続いてhello.pyを作成します。
import xlwings as xw
xw.Range('A1').value = 'はじめてのxlwings'
そして、このhello.pyを実行してみて下さい。

プログラムからA1で指定したセルに、文字列が入りました。
これが、xlwingsを利用してPythonからExcelを操作する最も簡単な方法です。
メリットとデメリット
- メリット:pipでxlwingsをインストールするだけで気軽にPythonからExcelの操作を始めることが出来る
- デメリット:Excel操作でPythonを実行することはできない
といった感じで、プログラミングの生産性は格段に上がるものの、Excel+VBAのマクロと比べると、使う側にとっては使い勝手があまりよくない、という状態ですよね。
というわけで、次は、Excel側からPythonプログラムを実行してみましょう。
Excel側からpythonプログラムを呼び出す
この章での説明は、前の章で作成したサンプルに対して変更を加える形で解説していきます。
ファイル構成:
HelloXlwings
|- hello.xlsx
|- hello.py
では解説始めていきます。
Excel側からPythonプログラムを呼び出すには、Excelにアドインを入れます。
pipでxlwingsをインストールするとxlwingsコマンドが使えるようになっているので、次のようにしてアドインをインストールします。
> xlwings addin install
xlwings 0.19.4
Successfully installed the xlwings add-in!
Please restart Excel.
メモ
このコマンドを実行する時、Pythonのインストールフォルダやカレントフォルダのパスに全角文字があると失敗するため注意。
もしExcelを開きっぱなしの場合は再起動しましょう。
すると、リボンに「xlwings」が追加されています。

続いて、hello.pyのプログラムを変更します。
import xlwings as xw
def main():
# 呼び出し元のExcelブックを取得
# Excelウィンドウが複数立ち上がっていても安心
hello_book = xw.Book.caller()
# 最初のシートを取得
sheet1 = hello_book.sheets[0]
# A2セルを取得
a2 = sheet1.range('A2')
# A2セルに文字列を設定
a2.value = 'Run mainから呼び出された'
「Run main」をクリックして実行してみましょう。

なんとなく予想はついていたかもしれませんが、A2セルに、pythonプログラムで指定した "Run mainから呼び出された" が入りました。
(A1セルにある「はじめてのxlwings」は前章のサンプルを実行した時に入ったものです)
ノート
今回インストールしたアドインに関しての公式のドキュメントはこちら
>> アドイン xlwings dev ドキュメント
Run mainボタンが使える条件
Run mainボタンは、次の条がそろった時に使うことができます。
- ExcelファイルとPythonプログラムが同じフォルダにあること
- hello.xlsxとhello.pyなど、ExcelファイルとPythonプログラムとで拡張子を除いたファイル名が同じであること
- Pythonプログラムにmain()関数が書かれていること
色々と面倒くさいと思うかもしれませんが、逆にこれさえ意識すれば、手軽にPythonプログラムを実行できるわけです。
メリットとデメリット
- メリット:Excelファイルを.xlsxとして保存できる(次章以降の説明では.xlsmで保存する内容になっていきます)
- デメリット:リボンから「xlwings」リボンの「Run main」をクリックしなければいけない(ボタンとかをトリガーにしたいですよね)
Excelの使い方しかわからない人でも、「xlwings」リボンの「Run main」をクリックすることはできるので、業務運用するにしても現実的かなと思います。
(とはいえ、次章以降で紹介する方法はもっと便利になっていきます)
ExcelマクロからPythonを呼び出す
いよいよこの章では、Excel+VBAマクロの組み合わせの使い勝手を損なわずに、内部の処理をPythonで置き換える方法を紹介します。
今回も、前章まで利用してきたサンプルプロジェクトを引き続き使って解説を続けていきます。
ファイル構成:
HelloXlwings
|- hello.xlsx
|- hello.py
Excelの参照設定にxlwingsライブラリを追加
Excelの参照設定にxlwingsライブラリを追加することで、VBAからRunPython()という関数が使えるようになります。
このRunPython()関数は、引数の文字列をPythonプログラムとして実行することができます。
hello.xlsxを開く→VibualBasicエディタを開く→
[ツール]→[参照設定]と進み、xlwingsへの参照にチェックを付けます。

するとVBAプロジェクトにxlwingsのプロジェクトが追加されます。

特にいじる必要はなく、xlsings(xlwings.xlam)と表示されていればOKです。
RunPython()関数にPythonプログラムを直接実行させる
それでは、pythonプログラムを呼び出すVBAマクロを書いてみましょう。
hello.xlsxを開いている状態で、以下の手順に従って進めて下さい。
[開発]リボン→[マクロ]を選択

[マクロの保存先]を "hello.xlsx" にする
[マクロの名前]を "python呼び出し" にする
そして作成ボタンクリック

すると空の python呼び出し() ルーチンが作成された状態でVisualBasicエディタが開くので、その中にRunPython()関数を記述していきます。

Sub python呼び出し()
PythonStr = "import xlwings as xw;" & _
"xw.Book.caller().sheets[0].range('a3').value" & _
"= 'どや?実行されるやろ?';"
RunPython (PythonStr)
End Sub
PythonStr変数に設定しているのは、前章で紹介した、セルに文字列を設定するPythonプログラムです。
ノート
マクロを作成すると、拡張子.xlsx(エクセルブック)ではなく.xlsm(エクセルマクロ)としてしか保存できなくなりますが、拡張子が変わっても問題なく動作するので、hello.xlsmとして保存しましょう。

このマクロを実行してみましょう。

RunPython()関数の中で実行したPythonプログラムが実行されていますね!

このように、RunPython()関数に渡した文字列は、Pythonプログラムとして実行されます。
pythonプログラムを.pyファイルに分離する
ごく短いプログラムであれば先ほどのように直接PythonプログラムをVBAの中に書くこともできるのですが、Pythonプログラムは外部ファイル(.py)に用意して、読み込んで実行することもできます。
先ほど紹介したRunPython()関数に渡す文字列の中で、他のPythonプログラム(.py)をインポートすればいいわけです。
まずはhello.pyの内容を書き換えます。
import xlwings as xw
def doyadoya():
xw.Book.caller().sheets[0].range('a4').value \
= 'どや?どやーーー!?'
続いてVBAマクロの記述を書き換えます。
Sub python呼び出し()
PythonStr = "import hello; hello.doyadoya();"
RunPython (PythonStr)
End Sub
さてこれで再度Python呼び出しマクロを実行すると

はい、先ほどと同じように動作しますね。
ノート
RunPython関数についての公式ドキュメントはこちら
>> VBA: RunPython - xlwings dev ドキュメント
quickstartを使うとプロジェクトのスケルトンを作ってくれる
前項ではExcelファイル(.xlsx)とPythonファイル(.py)を自分で作成し、ExcelマクロからRunPython()関数も自分で記述しました。
しかしquickstartコマンドを使うことで、一発でこの構成を作成してくれる機能が用意されています。
xlwings quickstart sample
このコマンドを実行すると、sampleフォルダが作られ、その中にsample.xlsmファイルとsample.pyファイルが作成されます。
sample
|- sample.xlsm
|- sample.py
自動生成されたsample.xlsmのVBAマクロ:
Sub SampleCall()
mymodule = Left(ThisWorkbook.Name, _
(InStrRev(ThisWorkbook.Name, ".", -1, vbTextCompare) - 1))
RunPython ("import " & mymodule & ";" & mymodule & ".main()")
End Sub
ややこしいですが、前項で紹介したRunPython関数()の使い方と同じことをしています。
import hello.pyと直接書かずに、Excelマクロファイル名からインポートするファイル名を推測しようとしているため、長ったらしいプログラムになっています。
自動生成されたPythonプログラム:
import xlwings as xw
@xw.sub # only required if you want to import it or run it via UDF Server
def main():
wb = xw.Book.caller()
sheet = wb.sheets[0]
if sheet["A1"].value == "Hello xlwings!":
sheet["A1"].value = "Bye xlwings!"
else:
sheet["A1"].value = "Hello xlwings!"
@xw.func
def hello(name):
return "hello {0}".format(name)
if __name__ == "__main__":
xw.Book("sample.xlsm").set_mock_caller()
main()
真ん中にあるhello(name)関数は後章でUDFs(User Defined Functions)として紹介するため、ここでは説明は省略します。
Excelマクロから呼び出される関数として、main()関数が定義されています。
SampleCallマクロを実行すると

Pythonのmain()関数が実行されます。

簡単な実装例をいくつか紹介
ここまでで、PythonからExcelを操作したり、ExcelからPythonを実行したりするといった、お互いを連携する基礎的な部分について一通り紹介しました。(UDFという自作関数の作成についてはまだ紹介していませんが)
この段階になると、プログラムをゴリゴリと記述したくなると思いますので、PythonでExcelを操作するためのxlwings APIの使用例をいくつか軽く紹介しておきます。
呼び出し元のExcelブックを取得する
複数のExcelブックを跨いだ処理をするのでもない限り、決まり文句的に使えばオッケーです。
xw.Book.caller()
=> <Book [hello.xlsx]>
セルを指定して値を取得・設定する
"A1"セルの値を"A6"セルにコピーするサンプルです。
sheet = xw.Book.caller().sheets[0]
a1value = sheet.range('a1').value
sheet.range('a6').value = a1value
範囲(レンジ)を指定して値を取得する
range()メソッドに指定する文字列をコロンで区切ることで範囲を指定することができます。
こんな感じのデータがあり

sheet = xw.Book.caller().sheets[0]
vals = sheet.range('a1:b3').value
sheet.range('d1').value = vals
実行すると

このような感じの結果になります。
DataFrameから表として扱う
Excelデータをpandasで扱いたい方向けの内容です。
こんな感じのExcelデータからDataFrameとして読み込むこともできます。

import pandas as pd
import xlwings as xlwings
a1range = xw.Book.caller().sheets[0].range('a1')
df = a1range.expand().options(pd.DataFrame).value
print(df)
=> 名前 年齢 持ち金
101.0 太郎 10歳 250円
105.0 花子 11歳 500円
302.0 ケンヂまる 35歳 10円
このサンプルの説明は長くなるので、別記事で解説予定ですが、まぁこのくらいシンプルなプログラムでできちゃいますという紹介ということで。
xlwings公式サイトのpython APIを見ながらプログラムを組もう
これらの実装例は、公式サイトのpython AP - xlwings dev ドキュメントのページを見ることで応用可能です。
デバッグする
xlwings実行中のPythonプログラムでエラーが発生すると、こんな感じで例外のトレースが表示されます。

メッセージを解析することでもある程度はデバッグ可能ですが、やっぱりデバッグモードで実行したいですよね?
そんな時は、Pythonプログラムを直接、デバッグモードで実行させればオッケーです。
ただし、Pythonプログラムを直接実行させた場合、Excelとの関連が何もない状態なので、
xw.Book.caller()
この命令がうまく機能しません。
そのため、Pythonプログラムであらかじめmockを設定した後、普通に開発環境のデバッグモードで実行します。
では実際に、サンプルプログラムを用意して、実行してみましょう。
range()メソッドには"a1"のように指定しなければいけないところ、"a-1"と指定してしまっているため、実行時エラーが出てしまう関数sample()を作りました。
このプログラムは、エントリポイントとして実行された時のみset_mock_caller()を使ってmockを設定しています。
def sample():
r = xw.Book.caller().sheets[0].range('a-1')
r.value = "動かないプログラム"
if __name__ == '__main__':
# xw.Book.caller()とした時に"hello.xlsm"を
# 返すようにモックを設定する。
xw.Book('hello.xlsm').set_mock_caller()
sample()
デバッグモードで実行すると、設定したブレークポイントでちゃんと止まり、エラーの原因追及が容易になります。

ちなみに、 xw.Book.caller() を参照しないプログラムでは、今回のようにmockを指定する必要はなく、そのままPythonプログラムをデバッグモードで実行させるだけで大丈夫です。
Excel関数を自作する
xlwingsを使うと、Excel用の関数を作ることもできます。
UDFs(User Defined Functions)と呼ばれるこの機能を利用すれば、VBAマクロから部分的にPythonの機能を呼び出すだけでなく、表から関数を呼び出す知識しかない人に自作関数を利用してもらうこともできるようになります。
前準備:VBAプロジェクト オブジェクト モデルへのアクセスを信頼する
自作関数を作成する環境で一度だけ、次の手順の実施が必要です。
Excelの[ファイル]リボン→[オプション]を選択

「Excelのオプション」ダイアログが開いたら、[トラストセンター]→[トラストセンターの設定]と選択します。

「トラストセンター」ダイアログが開くので、[マクロの設定]→[VBA プロジェクト オブジェクト モデルへのアクセスを信頼する]をチェックして[OK]をクリックして閉じます。

ノート
自作関数を利用する側の環境ではこの手順は実施しなくても大丈夫です。
quickstartでスケルトンを作成
quickstartコマンドを使用すると、UDFsを作成するためのスケルトンを作成してくれます。
xlwings quickstart UdfSample
UdfSampleフォルダ、その中にUdfSample.xlsmとUdfSample.pyが作成されます。
UdfSample
|- UdfSample.xlsm
|- UdfSample.py
次に、UdfSample.pyを開き、不要な行の削除し、自作関数の名前・シグネチャを決定します。
import xlwings as xw
# これがUDFですと示すためのアノテーション
@xw.func
def んを〇にする(name):
# 「ん」を「〇」にする。「うんこ」とか表示しなくて済む。
return name.replace('ん', '〇')
書き換えたポイント:
main()関数と if __name__ == "__main__" の部分は不要なため削除しました。
def hello(name)となっていた部分をdef んを〇にする(name)と書き替えました。
ポイント
自作関数には日本語の名前をつけることができます!
UdfSample.xlsmを開き、「xlwings」リボン→「fx Import Functions」

ノート
この時もしエラーが表示された場合、「VBAプロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックが入っているかもう一度確認しましょう。
エラーが表示されなければ成功です。
VisualBasicエディタを開くと、xlwings_udfsというモジュールが作成されています。

このモジュールを開くと、先ほどUdfSample.pyで作成した関数とシグネチャが定義されているのが確認できます。

これで、自作関数は完成です。
自作関数を使ってみる
まずはVisualBasicエディタのイミディエイトウィンドウで動作確認してみましょう。
(WindowsだとCtrl+Gで開きます)

イミディエイトウィンドウが表示されたら、自作関数を使ったプログラムを書いて、行末でEnterキーを押して実行してみます。

意図した通り「ん」が「〇」になってますね。

では、Excelシートに数式として使ってみましょう。

ちゃんと動作していますね!

作った自作関数を他のプロジェクトでインポートしてみる
xlwings_udfsを右クリック→[ファイルのエクスポート]
UdfSampleフォルダの下に適当な名前で保存します。
今回は、UdfSample.basという名前にしましょう。


これで、自作関数を他のExcelなどでインポートして使えるようになりました。
自作関数をインポートして動作確認するために、UseUdfSampleフォルダを作り、そこに「オレUdfSample使う.xlsm」を作成しましょう。
そして先ほど作成した、UdfSample.basとUdfSample.pyを同じフォルダにコピーしてきます。
UseUdfSample
|- オレUdfSample使う.xlsx
|- UdfSample.bas
|- UdfSample.py
オレUdfSample使う.xlsmを開き、VisualBasicエディタを開きます。
VBAProject右クリック→[ファイルのインポート]

同じフォルダにコピーしておいたUdfSample.basを選択します。

標準モジュールにxlwings_udfsが追加され、コードを見てみるとUdfSample.xlsmからエクスポートしたコードと同じになっています。

これで自作関数をUseUdfSample.xlsmから使う準備は完了です。
あとは前項で説明したように、VBAマクロの中やセルの数式から関数を使用することができます。
あとがき
このページで紹介した内容は、理解できましたか?
もし理解できない部分などありましたら、気兼ねなくコメントにてお知らせ下さい。