Python で RPA を実装するのにお手軽な PyAutoGUIは、残念なことに Pulover’s Macro Creator にあったようなウィンドウ操作系の関数(コマンド)を持っていません。これには色々理由があると思うのですが、恐らく、OS間(GUI)の互換性を維持するためというのもあると思います。
とはいえ、マウスの座標指定やキーボード操作だけでは本当に簡単な、単一のアプリケーションでの繰り返し作業くらいしかできませんよね? Pythonという言語(とパッケージ群)のもつ力を活用すれば、それでも活用の方法はあります。ただ、ちょっと大変です。さすがに、 RPA ツール使おうと思っちゃいますよね?
ただ、 PyAutoGUI 単体でなければウィンドウ操作系の関数を利用することができます。pip でインストールした際に同時にインストールされる PyGetWindow パッケージを使います。
PyAutoGUIのインストールや、Pythonの対話モードの使い方などは前回の記事を参照してください。
本記事は、Python, プログラミングの初心者が RPA づくりをしながらプログラミングを学習する、欲張りな連載です。
- import時に別名を付ける方法
- PyGetWindowを使ったWindow操作の基本
- 変数、配列の基礎の基礎
- 関数・メソッドの使い方
PyGetWindowを使う……前にimport
前回の記事の通り、pythonのパッケージはインストールするだけでは使えずに、importしないと使えません。
そして、importしたパッケージに入っている関数などは、pyautogui.moveTo()のように、importしたパッケージ名と.でつないで使用(呼び出す)必要がありました。
これは基本的な約束事、Pythonのルールです。しかし、pysutogui.とか、pygetwindow.とか、毎回タイピングするのは少し面倒です。
そこで、今回は、
import pyautogui as ag
import pygetwindow as gw
と、対話モードを起動したら最初に入力しましょう。この、import hogehoge as foo という書き方は、パッケージやモジュールなどに「別名・省略名」をつける記法です。英語のas なので、ag を pyautogui と同じだと考える、みたいに覚えるといいでしょう。
こうすると、以降、対話モードを終了するまでは
pyautogui.moveTo(x, y)
と書く代わりに、
ag.moveTo(x, y)
と書くことができます。
この書き方は、Python では広く使われていて本当のPythonの基礎を飛ばして入門書を読むと、当たり前に使われていて混乱することもあるので、意味をしっかり確認しておきましょう。
Python で広く使われているパッケージのnumpyやpandasなどでは、それぞれに慣例的に使われる別名、略称があります。
numpyであればnp, pandasであればpdがそれぞれよく使われます。
ネット上の記事など、一部断片的な情報提供では、この慣例的な略語を、importを省略して使う場合があります。その情報を求めるなら知っていて当然、ということですね。ただ、 RPA から入る皆さんのように、プログラミングの入り口は様々です。
慣れればすぐ分かることですが、いずれ RPA から他分野に進出したときに混乱しないように、asの意味と文化を理解しておきましょう。
PyGetWindow を使ったWindow操作
importは終わりましたね? 本連載では、gwをPyGetWindowの別名として使っていきます。
開かれているWindow 情報の取得
では、PyGetWindowでどのようにウィンドウ情報が扱われているのか見るために、まずは今開かれているウィンドウの情報を取得・表示していきましょう。
開かれているウィンドウのタイトルを表示するのは、getAllTitles()関数です。
>> gw.getAllTitles()
['', '', '', 'コマンド プロンプト - py', 'PyGetWindow — PyGetWindow documentation - Google Chrome', 'Discord', {...中略...} '', '', '', 'Program Manager']
このような結果が得られたと思います(もちろん、{…中略…}は、見やすくするためにいれたものなので、実際には表示されません)
getAllTitles()関数については、PMC で RPA を作った方にはなんとなく馴染みがあるのではないでしょうか。ウィンドウのタイトルバーに表示されている、ウィンドウの名前です。昔は、ほとんどのウィンドウにウィンドウタイトルが表示されるタイトルバーがついていたのですが、最近ではChrome などブラウザに代表されるように、タイトルバーがないウィンドウも増えています。
とはいえ、コマンドプロンプトは昔ながらのタイトルバーがついているので、getAllTitles()の結果と自分が開いているコマンドプロンプトのタイトルバーと見比べてみると分かりやすいでしょう。
また、ウィンドウのタイトルが角括弧[]で囲まれ、コンマで区切られているのはgetAllTitles()の結果が配列であるという意味です。配列については、今のところは、「複数の数字や文字列をひとまとまりにするのに便利な方法」くらいに思っていてください。
では、このウィンドウのタイトルを使ってウィンドウを操作していきます。
メモ帳を操作
では、ウィンドウタイトルが分かりやすい、メモ帳を操作してウィンドウタイトルからウィンドウを操作する方法を解説します。
メモ帳を起動しておきます。以後はメモ帳は閉じないで起動したままにしてくださいね。
memo = gw.getWindowsWithTitle('メモ帳')[0]
memo.activate() #ドキュメントによってはfocus()となっていますが、現在はactivate()が正しいようです
memo.maximize()
memo.restore()
動作が分かりやすいので、順に自分の手で入力していってくださいね。
さて、実行していくと、memo.activate()を実行した時点でコマンドプロンプトやPowershellからメモ帳にフォーカスが移ったのが分かったと思います。RPA で指定のウィンドウを操作したいときには便利に使えますね。
続く、maximize()関数でウィンドウの最大化、restore()関数で元に戻しています。
ここでノンプログラマの皆さんが気になるのは
memo = gw.getWindowsWithTitle('メモ帳')[0]
だと思います。
左から順に説明します。
まず、memo ですが、これは変数と呼ばれるものです。数学で出て来たx や y とほぼ一緒で、プログラミングにおいては、数字や文章、画像など色々なものを保存しておくことができます。いくつも作れて、自由に名前を付けられるクリップボードのようなものと考えるとPC上では分かりやすいと思います。
※半角スペースは、読みやすくするために入れています。
続く = ですが、これも数学とほぼ同じで代入を意味します。右辺の内容を左辺にコピーしています。つまり、変数 memo に、何かをコピーして保存しているということですね。
では、何を代入しているか? というと、「gw.getWindowsWithTitle(‘メモ帳’)[0]を実行した結果」と言えます。
getWindowsWithTitle()関数は、最初に述べた通りウィンドウのタイトルからウィンドウを特定する関数です。ここでは、タイトルに「メモ帳」と入っているウィンドウを探しています。では、[0]というのはなんでしょうか?
実は、getWindowsWithTitle関数は、複数のウィンドウが見つかったら複数のウィンドウの情報を結果とします(結果や値を[返す]、と言います)。メモ帳も複数のウィンドウを起動できますよね?
そういう複数の値・結果を返す関数は、通常配列で結果を返します。先ほど説明した通り、配列は「複数の数字や文字列をひとまとまりにする方法」です。画面に表示するときは[]に囲まれて、,で区切られています。
その配列の中から、一つの値(要素)を取り出すときに使うのが、[]角括弧の中に数字を入れる、[0]のような記法となります。
Python に限らず、コンピューターの世界では数字はゼロから数えることが常なので、[0]は、配列の中の一番目、[1]は配列の中の2番目……となっています。
皆さんおなじみのExcelでも、A1, A2, A3…のようにして、各列の数値を参照することがあると思います。その1, 2, 3が0から始まり、括弧でくくる必要がある、というイメージです。A[0], A[1], A[2]…といった感じですね。なお、変数Aに配列を代入した場合は、実際このようなアクセス方法になりますよ。
ということで、
memo = gw.getWindowsWithTitle('メモ帳')[0]
というのは、「メモ帳という文字がタイトルに含まれるウィンドウのうち、1番目に見つかったものを変数memoに代入する」という意味になります。
第2回にしてちょっと大変でしたね! ただ、ここでは「こんな風に書くんだ」という理解でも構いません。繰り返し使ううちに感覚的に理解できるでしょう。
では、改めて2行目以降を見てみると気づくことはないでしょうか?
そう、gw.ではなく、memo.となっています。memoは変数で、importしていないですよね?
これは、memo.という変数にウィンドウを入れたことで、ウィンドウを操作する機能(メソッドと言いますが、ここでは関数とほぼ一緒だと思ってください。試験でもなければ、言い間違えてもさほど問題はありません)が使用できるようになったためです。
流れとしては、
ウィンドウを探す→ウィンドウを保存する→ウィンドウに備わった機能を利用する
となっています。
筆者が個人的にノーコード RPA とプログラミングによる RPA の一番の違いは、変数を意識するかしないかだと思っています。
sのような概念はノーコード RPA にはありませんが、結局のところ「機能の利用」が関数の利用ですので、それほど大きな違いではありません。
一方で、変数については作成者が自分で名前をつけて管理する必要があるため、慣れるまで取っつきづらいです。一方で、慣れてしまうと、変数にいろいろなものを代入して機能を利用する「準備」をするようになります。そうすると、変数を使わないでもできるだけ実行できるノーコード RPA が扱いづらいと感じてきます。
また、RPA ツールは人間の行為を主体で考えますが、Python のプログラミングではウィンドウやファイルといったモノを中心に考えることが多いです(これはオブジェクト指向と言います)。
どちらがいい、悪いというものではないですが、 RPA を通じてPCに詳しくなっていずれはプログラマーに! と考えるのであれば、頑張って最初からプログラミング言語である Python でRPAを作った方が得ですね。
ウィンドウを変数に保存して機能を利用……ってなにやってるの?
※この節はより詳しい解説を読みたい方向けです。初心者の方は意識しなくてもいい内容です。
メモ帳の操作で、「変数にメモ帳を代入」して機能を操作しました。しかし、メモ帳は Python プログラムで作ったものではないですよね。これはどういうことでしょうか。
もう少し、PyGetWindow の動作をみるために、今開いている全てのウィンドウを取得する、getAllWindows()を実行してみましょう。
>>> gw.getAllWindows()
[Win32Window(hWnd=65826), Win32Window(hWnd=65990), Win32Window(hWnd=65962), {...中略...} Win32Window(hWnd=131562)]
こちらも、配列で結果が出ていますね。もし、すべてのウィンドウの中の、最初の一つを操作したい場合は、 window = gw.getAllWindows()[0]とすればいいことが分かります。
さて、getAllWindows()の結果の方は直感的に理解しづらく、「これを理解しなきゃプログラミングはマスターできないのか」なんて思ってしまうかもしれません。が、大丈夫です。実は今、ウィンドウズでアプリを作っている人でも1/3くらいはこのhWnd=….を見ても、何を意味しているのか分からないと思います。
ここでは、hWnd=….というようなのが、ウィンドウハンドル、ウィンドウハンドルとは、ウィンドウを操作するための整理番号だと覚えておけば十分です。整理番号というのは、病院や役所に行ったときに、受付の機械からとった紙に印刷されている、あの数字です。
整理番号なので、Windowsを起動する度に、アプリケーションを起動する度に、同じアプリケーションでもこの数字は異なります。なので、「番号が振られるんだな」と覚えておけば、数字なんて一切見なくて大丈夫です。覚える必要もありません。
逆に言うと、ウィンドウハンドルでウィンドウを操作するためには、毎回どうにかして、自分が操作したいウィンドウと、ウィンドウハンドルを結びつける必要があります。
getWindowsWithTitle()関数では、ウィンドウのタイトルでウィンドウを絞り混んで(Excelのフィルター機能を想像してください)、これらの結果を返してくれていました。
また、hWnd=…を、Win32Window()というのが囲んでいると思います。これが、「ウィンドウを操作する機能とデータの塊(オブジェクト)」です。実際に自分でプログラムするととても大変ですが、PyGetWindowパッケージが全て任せてやってくれているので、私たちは深くまで理解しないでも、「タイトルからウィンドウを探して操作する」ということが可能になっています。
おわりに
第2回にして、早くもちょっと大変になってしまいました。
- Python の RPA では、「変数」というクリップボードのようなものをよく使う
- 配列は[]をつけるとひとつひとつの値(セルのようなもの)にアクセスできる
- ウィンドウを操作する機能はぱっと見難しいけれど、使い方さえ分かれば大丈夫
とはいえ、変数の扱い方は慣れていかないと大変ですが、その他の難しい部分については RPA をやる上ではまったく必須ではありません。プログラミングの経験を積んでいく中で、なんとなく「こういう書き方をよくするな」と馴染んでいった後で、改めて、「あ、これもそういうことか」と理解できると思います。
もちろん、最初から教科書をしっかり読んで勉強しても構いませんが、このシリーズではとにかく「画面が動いて」「役に立つ」ものを作りながら Python プログラミングを進めて行きます。
とはいえ、まだまだ RPA を実現できるほどの機能を解説していないので、次回も早めに更新できるように頑張ります。
3件のフィードバック