エクセルをPythonで操作したい!〜xlwings×Pyinstaller〜
分析屋のn.kです。よろしくお願いします!
背景
エクセルシート上でデータ⼊⼒⇒ポチッとボタンを押すとPythonが⾛る⇒同じシート上に結果出⼒というアプリを作る必要があり、調べました。
※アプリとして配るため、導⼊先のPCにPython環境が無いという状況です。
結論
以下3⼿順を踏むことで実装可能です。
1. エクセル上でPythonを呼び出すために… エクセルアドイン&Pythonモジュール「xlwings」を使⽤する
2. Python環境がなくてもPythonが実⾏できるように… パッケージ化ツール「Pyinstaller」を使⽤する
3. 1と2を連携させる
では⼿順を追っていきます。
1. エクセルでPythonを呼び出す「xlwings」
xlwingsはPythonライブラリの⼀つで、Excelを開きながらPythonでExcelを操作することが可能です。
ただし、エクセルから何か操作(ボタン押下など)をしてPythonコードを⾛らせるためには、エクセルアドインとしてのxlwingsの導⼊も必要になります。
参考
pip install xlwings したらいよいよ、
エクセルマクロから、「hello.py」を呼び出すことを考えます。
1. 「hello.py」を作成する
# hello.py
import numpy as np
# xlwingsライブラリのインポート
import xlwings as xw
import sys, os
# エクセルマクロから関数を呼び出す必要があるので定義する
def world():
# 後々のアプリ化を考え、このコードからの相対パスで各種ファイル場所を指定する
# sys.executableで現在地パスを取得することがポイント
program_directory = os.path.dirname(os.path.abspath(sys.executable))
os.chdir(program_directory)
# ブックを開く
# こちらも相対パスで。
wb = xw.Book('myproject.xlsm')
# sheetオブジェクトをインスタンス化: # Sheet1タブを取得する
sheet = wb.sheets['Sheet1']
# rangeの値の読み込み/書き込みが簡単にできる:
# A1セルに値を書き込む
sheet.range('A1').value = 'Foo 1'
⚠ ソースコードの現在地は「sys.executable」で取得しないと、Pyinstallerでパッケージ化した時に相対パス指定が上⼿くいかないので要注意。下記は動きません。
× __file__
× os.getced()
× Path( file )
参考
2. xlwingsアドインが⼊った.xlsmを作成する
ターミナルで以下のコマンドを打つと myproject.xlsm が作成されます。
xlwings quickstart myproject --standalone
⽣成されたmyproject.xlsmのマクロを開き(Alt+F11)、プロジェクト欄に「標準モジュール>xlwings」が表⽰されていれば導⼊完了です。
3. hello.pyを呼び出すVBAを新規作成する
「標準モジュール」に新しいモジュールを作成し、以下のVBAを登録して閉じます。先ほど作成したpythonのworld関数を呼び出しています。
Sub HelloWorld()
RunPython "import hello; hello.world()"
End Sub
4. エクセルにVBAを張り付けたボタンを配置し、実⾏!
「開発タブ>挿⼊>ボタン」から以下の画⾯を呼び出し、先ほど保存した「HelloWorld」VBAを選択し、OK。
ボタンを押して実⾏します。A1に値が書き込まれています。
2. Python環境がない⼈に.pyを使ってもらう「Pyinstaller」
Pyinstallerは、Python環境がないユーザーにexe化してアプリ提供する、いわゆる「Pythonフリーザー」ツールのうちの1つです。
メリット
他のツール(Python embeddable、cx_Freeze、py2exeなど)よりもドキュメントが多く最も⼀般的。
.exe形式で配布できるのも唯⼀。
ワンコマンドで依存関係にあるオリジナルコード、配布ライブラリ、Python本体を1つにパッケージ化できるのがかなり簡便。
デメリット
呼び出しに時間が掛かるのは難点。.exeファイルサイズが⼤きくなりがち。
参考
pip install pyinstaller
したらいよいよ、
hello.pyをPython本体ごと.exe化することを考えます
1. hello.pyをアプリ化⽤に少し書き換える
# hello.py
import numpy as np
# xlwingsライブラリのインポート
import xlwings as xw
import sys, os
# エクセルマクロから関数を呼び出す必要があるので定義する
# 更新:main()関数とした
def main():
# 後々のアプリ化を考え、このコードからの相対パスで各種ファイル場所を指定する
# sys.executableで現在地パスを取得することがポイント
program_directory = os.path.dirname(os.path.abspath(sys.executable))
os.chdir(program_directory)
# ブックを開く
# こちらも相対パスで。
wb = xw.Book('myproject.xlsm')
# sheetオブジェクトをインスタンス化:
# Sheet1タブを取得する
sheet = wb.sheets['Sheet1']
# rangeの値の読み込み/書き込みが簡単にできる:
# A1セルに値を書き込む
sheet.range('A1').value = 'Foo 1'
# 更新: main 内にmain()関数を指定した
if __name__ == '__main__':
xw.Book("myproject.xlsm").set_mock_caller()
main()
2. hello.pyから.exeを⽣成する
ターミナルで以下のコマンドを打ち、結果を確認します。
pyinstaller hello.py -–onefile -–noconsole
以下のようなフォルダ構成になります。「dist」内に.exeが格納されています。
3. フォルダ構成を少し修正
ソースコードの現在地は.exeの場所になるため、distフォルダ内に参照したいフォルダやファイルを配置します。
デプロイの際は、この distフォルダ を渡す形となります。
3. 1.2を連携させてデプロイ!
Pyinstallerでパッケージ化した.exeをVBAで呼び出すには、マクロの記述を少し変更する必要があります(デプロイ - xlwings Documentation)
1. myproject.xlsmのVBAを少し修正
Sub SampleCallFrozen()
RunFrozenPython ThisWorkbook.Path & "\hello.exe"
End Sub
2. VBAを張り付けたボタンを配置する
3. ボタンを押して実⾏
distは.zip化して配布しても〇です。
まとめ
Python環境が無い⼈にまるっとPython+ライブラリ+実⾏コードを渡す「Pyinstaller」。エクセルとPythonを連携させる「xlwings」。
2つを連携させることでPython×エクセルアプリが誰にでも配布できるようになりました。
エクセルではできないような複雑な処理(グラフ描画も〇)はPythonにまかせて、快適なエクセルライフを送りましょう。
(コードの現在地取得で1⽇ハマったので、みなさんのお役に⽴つと幸いです)
株式会社分析屋について
ホームページはこちら。
noteでの会社紹介記事はこちら。
【データ分析で日本を豊かに】
分析屋はシステム分野・ライフサイエンス分野・マーケティング分野の知見を生かし、多種多様な分野の企業様のデータ分析のご支援をさせていただいております。 「あなたの問題解決をする」をモットーに、お客様の抱える課題にあわせた解析・分析手法を用いて、問題解決へのお手伝いをいたします!
【マーケティング】
マーケティング戦略上の目的に向けて、各種のデータ統合及び加工ならびにPDCAサイクル運用全般を支援や高度なデータ分析技術により複雑な課題解決に向けての分析サービスを提供いたします。
【システム】
アプリケーション開発やデータベース構築、WEBサイト構築、運用保守業務などお客様の問題やご要望に沿ってご支援いたします。
【ライフサイエンス】
機械学習や各種アルゴリズムなどの解析アルゴリズム開発サービスを提供いたします。過去には医療系のバイタルデータを扱った解析が主でしたが、今後はそれらで培った経験・技術を工業など他の分野の企業様の問題解決にも役立てていく方針です。
【SES】
SESサービスも行っております。