t-hom’s diary

主にVBAネタを扱っているブログです。

ひと足先にPythonでCOMを利用したExcel操作をやってみる

MicrosoftExcelPythonを搭載することを真剣に検討しているらしい。
www.itmedia.co.jp

まだ検討の段階なので、どんな実装になるかは明らかにされていないが、基本的にはVBAと同じくCOMの操作になるんじゃないかと思う。

今回はひと足先に、PythonでCOMを利用したExcel操作をやってみようと思う。

参考にしたサイトはこちら。
Python Win32 Extensions - MyMemoWiki

COMとは

COMとは「コンポーネント・オブジェクト・モデル」の略で、様々なプログラミング言語から呼び出すことができるソフトウェア部品の技術仕様として1997年にMicrosoftが発表したものである。
.NET登場以前はWindowsの標準的な技術であり、現在も数多くのソフトウェアがCOMに依存している。

COMではオブジェクトをメモリにどういう風に配置するか、自身が持つプロパティやメソッドを呼び出し元にどうやって伝えるかといった細々とした仕様が取り決められているので、COMの仕様に従った部品は様々な言語から利用することができる。

Excel VBAも、実はVBA言語でExcelのCOMオブジェクトを操作する行為であり、例えばRange("A1").Value = "Hello"という命令はVBAというよりも、ほぼCOMの操作に近い。

厳密にいえば、以下の特徴はCOMではなくVBAの仕様である。

  • イコールで代入する
  • オブジェクトのプロパティにはドットでアクセスする
  • 文字列はダブルクォーテーションで囲む
  • 大文字、小文字を区別しない

ただ最初の3つは多くの言語で採用されているので、Pythonで書いてもJavaで書いてもRubyで書いても、Range("A1").Value = "Hello"となる。そうでない言語もあるかもしれないが。

PythonでのCOM操作(準備編)

Pythonのインストールは多くのサイトで解説があるので本記事では省略する。
PythonでCOMを利用するためには、別途「Python Win32 Extensions」のインストールが必要である。

インストールするべきWin32 ExtensionsのバージョンはPythonのバージョンによって異なるため、まずはPythonのバージョンをチェック。
コマンドプロンプトを起動し、pythonと入力するとバージョンが表示される。
f:id:t-hom:20171224200547p:plain

そして以下リンクよりSourceForgeへアクセスし、
https://sourceforge.net/projects/pywin32/files/


pywin32のフォルダリンクを開く。
f:id:t-hom:20171224201057p:plain

するとビルドの一覧が表示される。ここは一番新しいもので良い。
f:id:t-hom:20171224201230p:plain

ここからちょっとややこしいけれど、使っているPCのCPUがAMD製かIntel製かによって変わる。
上半分のamd64と付いたものがAMD用、下半分がIntel用になっている。
f:id:t-hom:20171224202131p:plain

私の環境はIntel製CPUで、Pythonのバージョンが3.6.2なので、pywin32-221.win32-py3.6.exeをダウンロードした。

インストールは英語だけれど、「次へ、次へ、次へ、完了」と進めるだけなので特に問題ないと思う。

PythonでのCOM操作(実践編)

さて、では実際にExcelを操作してみよう。
先ほどバージョン確認のためにPythonを起動したが、これはexit()を入力して終了させて、再度pythonコマンドで起動しておく。
また、起動しているExcelがあれば終了させておいたほうが分かりやすい。

そして、入力待ちのプロンプト「>>>」が表示されたらimport win32com.clientと入力してEnter。

>>> import win32com.client

これは先ほどインストールしたPython Win32 Extentionsを読み込むための命令で、成功すれば単に次の入力プロンプト「>>>」が表示される。
他にズラズラと表示されてたらエラーなのでスペルミスがないか、インストールに成功しているか再確認する。

次に、変数xlAppにExcelを代入する。

>>> xlApp = win32com.client.Dispatch("Excel.Application")

ややこしい操作だけれど、VBScript のSet xlApp = CreateObject("Excel.Application")に相当する操作だと思っていだけると良い。
Pythonには変数宣言が存在しないので、単に変数名に値を代入するという操作になる。
また、VBAの場合はオブジェクトの代入にはSetを使うが、Pythonは数値や文字などと同じく単に代入するだけである。

さて、この段階でExcelがバックグラウンドで起動されているが、画面上はまだ表示されていない。
次に、Excelを表示させる。

>>> xlApp.Visible = -1

xlAppはExcel VBAのApplicationと同じように扱える。VBAの場合はApplication.Visible = Trueと書くが、VBAではTrueの値が-1なのに対し、PythonではTrueの値が1なので、ここでは-1を指定している。

ただVBAは0をFalse、0以外をTrueと判定するので、xlApp.Visible = Trueと書いても動作する。以降は分かりやすいようにPythonのTrue、Falseを使って書く。

さて、ここまで来たら、基本的なCOM操作はVBAと変わらない
ワークブックを追加してシート名をSampleに変え、A1セルにHelloと入力してクローズする処理をやってみる。

>>> wb = xlApp.Workbooks.Add()
>>> sh = wb.Sheets(1)
>>> sh.Name = "Sample"
>>> sh.Range("A1").Value = "Hello"
>>> xlApp.DisplayAlerts = False
>>> wb.Close()
>>> xlApp.DisplayAlerts = True
>>> xlApp.Quit()

コマンドを入力してEnterするたびに、処理が実行されるのが分かる。
ちなみに上記の処理をVBAで書くとこうなる。

    Set xlApp = Excel.Application
    Set wb = xlApp.Workbooks.Add
    Set sh = wb.Sheets(1)
    sh.Name = "Sample"
    sh.Range("A1").Value = "Hello"
    xlApp.DisplayAlerts = False
    wb.Close
    xlApp.DisplayAlerts = True
    xlApp.Quit

VBAPythonの違いを見てみると、

  • オブジェクトの代入にSetをつけるかどうか
  • 引数のないメソッド呼び出しに()を付けるかどうか

という2点を除いて、あまり変わらない。

つまりCOMである以上、オブジェクトの操作感はそれほど変わらないということになる。
ではPythonだと何が嬉しいのかというと、豊富なライブラリ群と洗練された言語仕様である。

Pythonの機能を使ってマクロを作る

折角なのでPythonのライブラリを使ってExcelマクロを作ってみよう。

サンプルなのでVBAでも簡単にできるような内容にしておく。

>>> import win32com.client
>>> import datetime
>>> xlApp = win32com.client.Dispatch("Excel.Application")
>>> xlApp.Visible = True
>>> wb = xlApp.Workbooks.Add()
>>> ws = wb.Sheets(1)
>>> now = datetime.datetime.now()
>>> ws.Range("A1").Value = now.hour

これは最初にPythonのdatetimeライブラリを読み込んでおき、変数nowに現在日時を入れて「〇時」だけを取り出すマクロ。
Pythonのdatetime型はオブジェクトなので、hourプロパティで時刻を取り出すことができる。
ブログ執筆時点では夜9時なので、A1セルには21が挿入される。

VBAならNow()関数とHour()関数を組み合わせるところだが、こちらはVBA専用の関数なのでPythonからは利用できない。
またCollectionもVBAライブラリのクラスなのでPythonからは利用できない。
VBA自体もおそらくCOMなので、PythonからVBA関数を利用する方法はあるかもしれない。

更に、配列などのデータ型や、IfやForなどの構文もVBAとは異なる。
共通しているのはあくまでCOMの操作部分だけである。

for文を例にPythonの特徴を紹介

実はPythonVBAのようなFor文は無く、VBAでいうところのFor Eachが標準のfor文にあたる。
たとえば1から10まで出力させたい場合、VBAでは次のように書く。

For i = 1 to 10
    Debug.Print i
Next

Pythonではこうなる。

>>> for i in range(1, 11):
...     print(i)
...

ここでrangeはpythonの関数なのでExcelのRangeとは関係ないことに注意。
1~11を指定するとひとつ少ない1~10が作られるという、VBA使いには馴染みのない仕様である。

この...は>>>と同じく前行から続いているという意味のプロンプトなので自分で入力する必要はない。
for文の最後は必ずコロンを入力して改行する。そしてTabキーでインデントし、print(i)と入力してEnter。
これで終わりなら、プロンプト「...」でそのままEnterを入力すると1~10まで出力される。

また、VBAではブロックを開始と終了のステートメントで表すのに対し、Pythonではインデントで表す。
コードの見た目がそのまま文法になってしまうので自由にインデントすることはできない。
これにはスタイルの違いによる読みにくさが発生しないというメリットがある。

さて、では最後のサンプルとしてPythonのforでVBAのFor Eachと同じようにセルに対して使ってみよう。
次のコードを実行するとA1からA5にそれぞれHelloと入る。

>>> for r in ws.Range("A1:A5"):
...     r.Value = "Hello"
...

これでCOMの操作は同じであるが、標準関数や文法が異なるということがお分かりいただけたかと思う。

終わりに

ExcelPythonが標準搭載される意味はとても大きいと思う。
言語を選択できるようになれば、マクロとVBAの違いなども説明しやすくなる。また、何がExcelのCOM操作で、何が言語機能なのかがよりハッキリするので、Excel VBAの構成もよりクリアに認識できるようになると思う。

現在COM以外の方法でPythonからExcelを操作するライブラリなどもあるけれど、個人的には標準搭載するならCOM方式が良いのではないかと思う。

MicrosoftはCOMの後継にあたる.NETを推進しているが、.NET言語であるVB.NetC#を外してあえてPythonの搭載を検討するあたり、Excelの.NET対応は考えていないのではないかと思う。

COMならPythonのスッキリした文法と豊富なライブラリの恩恵を受けつつ、従来のVBAユーザーもPythonに移行しやすい。

今のところPythonは完全に動的型付け言語なので、入力補完などのIDE機能は期待できない。
手入力も面倒なので、この辺りはMicrosoftが何か考えてくれるといいなと思う。

さて、今回はすべてコマンドラインからの入力だったが、もちろんファイルに書いて実行することもできる。
興味があれば書籍や他のWebサイト等で調べてみると良いと思う。

本格的にPythonを学習される場合は以下の書籍がオススメ。

Pythonスタートブック

Pythonスタートブック

レビュー記事も書いているのでよろしく。
thom.hateblo.jp

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。