pywinautoでWindowsの画面操作を自動化する方法【実践コード付き】

業務を自動化するために、BatやPowerShellを活用している方も多いのではないでしょうか。

しかし、アプリのボタンをクリックしたり、設定画面を開いて項目を変更したりといった操作は、コマンドだけでは自動化が難しい場合があります。

「ここも自動化できれば、手作業を減らせるのに……」

そう思いながらも、結局は手作業で対応している方もいるはずです。

実際に私も同じような課題に直面し、自動化を断念して手作業で対応していました。

そんな課題を解決する手段の一つが、Pythonのライブラリであるpywinautoです。

pywinautoを使えば、Windowsアプリの画面やボタンなどのUI要素を特定し、クリックや文字入力といった操作を自動化できます。

本記事では、pywinautoの基本から、インストール方法、画面上の要素を特定する方法、実践コードを使った自動化手順まで、順番に詳しく解説します。

目次

pywinautoとは

pywinautoとは、Windowsの画面操作を自動化できるPythonのライブラリのことです。

人間が普段マウスやキーボードで行っている操作を自動化することができます。

他に自動化が可能なライブラリとしてpyautoguiというものもあります。

pyautoguiは座標や画像認識をベースとしたものになりますがpywinautoはUI要素(ウィンドウ、ボタン、入力欄など)を直接操作するものになっています。

そのため、座標のズレや認識の違いによる処理の中断は発生しにくく、より安定した自動化が可能です。

pywinautoによる自動化の方法

本題の自動化の方法について解説していきます。
主な流れとしては以下の通りです。

  • pywinautoのインストール
  • 操作対象の要素を特定
  • 自動操作コードの作成

それぞれの手順について詳しく解説していきます。
※すでにPythonの環境が出来上がっていることを前提とします

① pywinautoのインストール

まずはpywinautoをインストールしていきます。

▼ Terminalの場合 ▼

Python
pip install pywinauto

▼ Jupyter Notebookの場合 ▼

Python
!pip install pywinauto

② 操作対象の要素を特定

次に、自動化を行う操作の要素を特定していきます
(今回はメモ帳を例に挙げます)

まずは現在表示されているウィンドウの名称を以下のコードで列挙します。

Python
from pywinauto import Desktop

uia_window = Desktop(backend="uia")

for w in uia_window.windows():
    print(w.window_text())

以下、実行結果になります。

タスク バー
電卓
Jupyter Notebook
ボリューム (G:) - エクスプローラー
NVIDIA GeForce Overlay
タイトルなし - メモ帳
Program Manager

該当のウィンドウ名称が表示されない場合は、従来型のWindowsデスクトップアプリを対象とする「win32」で列挙します。

Python
from pywinauto import Desktop

win32_window = Desktop(backend="win32")

for w in uia_window.windows():
    print(w.window_text())

今回はメモ帳のため「uia」のほうで列挙された「タイトルなし – メモ帳」が対象になります。

次のtitle=""の部分に該当のウィドウ名称を記述することで、そのウィンドウに対する各要素が列挙されます。

Python
notepad = uia_window.window(title="タイトルなし - メモ帳")
notepad.print_control_identifiers()

今回は動作安定化のためtitle_reにて部分一致させ、さらに.*にて名称が多少異なる場合でも対応可能なようにします。

Python
notepad = uia_window.window(title_re=".*メモ帳")
notepad.print_control_identifiers()

以下がメモ帳における各要素になります。

Dialog - 'タイトルなし - メモ帳'    (L0, T0, R0, B0)
['タイトルなし - メモ帳', 'Dialog', 'タイトルなし - メモ帳Dialog', 'Dialog0', 'Dialog1']
child_window(title="タイトルなし - メモ帳", control_type="Window")
   | 
   | Pane - ''    (L-31994, T-31925, R-30792, B-31159)
   | ['Pane', 'タイトルなしPane', 'Pane0', 'Pane1', 'タイトルなしPane0', 'タイトルなしPane1']
   |    | 
   |    | Document - ''    (L-31994, T-31925, R-30792, B-31159)
   |    | ['タイトルなしDocument', 'Document']
   | 
   | Pane - ''    (L-31954, T-31990, R-31667, B-31958)
   | ['Pane2', 'タイトルなしPane2']
   |    | 
   |    | Pane - ''    (L-31954, T-31990, R-31667, B-31958)
   |    | ['Pane3', 'タイトルなしPane3']
   |    |    | 
   |    |    | TabControl - ''    (L-31954, T-31990, R-31667, B-31958)
   |    |    | ['TabControl', 'タイトルなしTabControl', 'TabControl新しいタブの追加']
   |    |    | child_window(auto_id="Tabs", control_type="Tab")
   |    |    |    | 
   |    |    |    | ListBox - ''    (L-31952, T-31990, R-31704, B-31958)
   |    |    |    | ['タイトルなしListBox', 'ListBox']
   |    |    |    | child_window(auto_id="TabListView", control_type="List")
   |    |    |    |    | 
   |    |    |    |    | TabItem - 'タイトルなし. 未変更。'    (L-31950, T-31990, R-31704, B-31958)
   |    |    |    |    | ['タイトルなし. 未変更。TabItem', 'タイトルなし. 未変更。', 'TabItem']
   |    |    |    |    | child_window(title="タイトルなし. 未変更。", control_type="TabItem")
   |    |    |    |    |    | 
   |    |    |    |    |    | Static - 'タイトルなし'    (L-31937, T-31982, R-31882, B-31967)
   |    |    |    |    |    | ['タイトルなしStatic', 'Static', 'タイトルなし', 'Static0', 'Static1']
   |    |    |    |    |    | child_window(title="タイトルなし", control_type="Text")
   |    |    |    |    |    | 
   |    |    |    |    |    | Button - 'タブを閉じる'    (L-31745, T-31986, R-31713, B-31962)
   |    |    |    |    |    | ['タブを閉じるButton', 'Button', 'タブを閉じる', 'Button0', 'Button1']
   |    |    |    |    |    | child_window(title="タブを閉じる", auto_id="CloseButton", control_type="Button")
   |    |    |    | 
   |    |    |    | Button - '新しいタブの追加'    (L-31701, T-31984, R-31669, B-31960)
   |    |    |    | ['新しいタブの追加', '新しいタブの追加Button', 'Button2']
   |    |    |    | child_window(title="新しいタブの追加", auto_id="AddButton", control_type="Button")
   | 
   | Pane - ''    (L-31994, T-31958, R-30792, B-31925)
   | ['Pane4', 'タイトルなしPane4']
   |    | 
   |    | Pane - ''    (L-31994, T-31958, R-30792, B-31925)
   |    | ['Pane5', 'タイトルなしPane5']
   |    |    | 
   |    |    | Menu - ''    (L-31994, T-31958, R-31814, B-31926)
   |    |    | ['タイトルなしMenu', 'Menu', 'Menu0', 'Menu1']
   |    |    | child_window(auto_id="MenuBar", control_type="MenuBar")
   |    |    |    | 
   |    |    |    | MenuItem - 'ファイル'    (L-31990, T-31958, R-31930, B-31926)
   |    |    |    | ['MenuItem', 'ファイル', 'ファイルMenuItem', 'MenuItem0', 'MenuItem1']
   |    |    |    | child_window(title="ファイル", auto_id="File", control_type="MenuItem")
   |    |    |    | 
   |    |    |    | MenuItem - '編集'    (L-31922, T-31958, R-31874, B-31926)
   |    |    |    | ['MenuItem2', '編集MenuItem', '編集']
   |    |    |    | child_window(title="編集", auto_id="Edit", control_type="MenuItem")
   |    |    |    | 
   |    |    |    | MenuItem - '表示'    (L-31866, T-31958, R-31818, B-31926)
   |    |    |    | ['MenuItem3', '表示MenuItem', '表示']
   |    |    |    | child_window(title="表示", auto_id="View", control_type="MenuItem")
   |    |    | 
   |    |    | Button - '見出し'    (L-31557, T-31958, R-31501, B-31926)
   |    |    | ['見出し', '見出しButton', 'Button3']
   |    |    | child_window(title="見出し", control_type="Button")
   |    |    | 
   |    |    | Button - 'リスト'    (L-31501, T-31958, R-31445, B-31926)
   |    |    | ['リスト', 'リストButton', 'Button4']
   |    |    | child_window(title="リスト", control_type="Button")
   |    |    | 
   |    |    | Button - '太字 (Ctrl+B)'    (L-31445, T-31958, R-31413, B-31926)
   |    |    | ['太字 (Ctrl+B)Button', '太字 (Ctrl+B)', 'Button5']
   |    |    | child_window(title="太字 (Ctrl+B)", control_type="Button")
   |    |    | 
   |    |    | Button - '斜体 (Ctrl+I)'    (L-31413, T-31958, R-31381, B-31926)
   |    |    | ['斜体 (Ctrl+I)Button', '斜体 (Ctrl+I)', 'Button6']
   |    |    | child_window(title="斜体 (Ctrl+I)", control_type="Button")
   |    |    | 
   |    |    | Button - '取り消し線 (Ctrl+Shift+X)'    (L-31381, T-31958, R-31349, B-31926)
   |    |    | ['取り消し線 (Ctrl+Shift+X)Button', 'Button7', '取り消し線 (Ctrl+Shift+X)']
   |    |    | child_window(title="取り消し線 (Ctrl+Shift+X)", control_type="Button")
   |    |    | 
   |    |    | Button - 'リンク (Ctrl+K)'    (L-31349, T-31958, R-31317, B-31926)
   |    |    | ['リンク (Ctrl+K)Button', 'リンク (Ctrl+K)', 'Button8']
   |    |    | child_window(title="リンク (Ctrl+K)", control_type="Button")
   |    |    | 
   |    |    | Button - '表'    (L-31317, T-31958, R-31261, B-31926)
   |    |    | ['表', '表Button', 'Button9']
   |    |    | child_window(title="表", control_type="Button")
   |    |    | 
   |    |    | Button - '書式設定のクリア (Ctrl+Space)'    (L-31261, T-31958, R-31229, B-31926)
   |    |    | ['書式設定のクリア (Ctrl+Space)Button', '書式設定のクリア (Ctrl+Space)', 'Button10']
   |    |    | child_window(title="書式設定のクリア (Ctrl+Space)", control_type="Button")
   |    |    | 
   |    |    | Button - '手書きツール'    (L-30958, T-31958, R-30902, B-31926)
   |    |    | ['手書きツール', 'Button11', '手書きツールButton']
   |    |    | child_window(title="手書きツール", auto_id="RewriteDropDownButton", control_type="Button")
   |    |    | 
   |    |    | Button - '新着情報'    (L-30898, T-31957, R-30868, B-31927)
   |    |    | ['新着情報', 'Button12', '新着情報Button']
   |    |    | child_window(title="新着情報", auto_id="FREButton", control_type="Button")
   |    |    | 
   |    |    | Button - 'ユーザー アバター'    (L-30864, T-31958, R-30834, B-31926)
   |    |    | ['ユーザー アバターButton', 'ユーザー アバター', 'Button13']
   |    |    | child_window(title="ユーザー アバター", auto_id="AvatarButton", control_type="Button")
   |    |    | 
   |    |    | Button - '設定'    (L-30830, T-31958, R-30800, B-31926)
   |    |    | ['設定Button', '設定', 'Button14']
   |    |    | child_window(title="設定", auto_id="SettingsButton", control_type="Button")
   |    |    | 
   |    |    | Dialog - ''    (L0, T0, R0, B0)
   |    |    | ['Dialog2']
   |    |    | child_window(auto_id="PrivacyTeachingTip", control_type="Window")
   |    |    | 
   |    |    | Pane - ''    (L0, T0, R0, B0)
   |    |    | ['Pane6']
   |    |    | child_window(auto_id="PsDownloadDetailTeachingTip", control_type="Pane")
   |    |    | 
   |    |    | Pane - 'この機能を使用するには、サインアウト中にローカル AI モデルのダウンロードが完了する必要があります。'    (L0, T0, R0, B0)
   |    |    | ['この機能を使用するには、サインアウト中にローカル AI モデルのダウンロードが完了する必要があります。', 'この機能を使用するには、サインアウト中にローカル AI モデルのダウンロードが完了する必要があります。Pane', 'Pane7']
   |    |    | child_window(title="この機能を使用するには、サインアウト中にローカル AI モデルのダウンロードが完了する必要があります。", auto_id="PsDownloadLightTeachingTip", control_type="Pane")
   | 
   | Pane - ''    (L-31994, T-31159, R-30792, B-31127)
   | ['Pane8', '  行 1、列 1Pane', '  行 1、列 1Pane0', '  行 1、列 1Pane1']
   |    | 
   |    | Pane - ''    (L-31994, T-31159, R-30792, B-31127)
   |    | ['Pane9', '  行 1、列 1Pane2']
   |    |    | 
   |    |    | Static - '  行 1、列 1'    (L-31978, T-31151, R-31919, B-31135)
   |    |    | ['  行 1、列 1Static', 'Static2', '  行 1、列 1']
   |    |    | child_window(title="  行 1、列 1", auto_id="ContentTextBlock", control_type="Text")
   |    |    | 
   |    |    | Static - '0 文字'    (L-31889, T-31151, R-31855, B-31135)
   |    |    | ['0 文字', 'Static3', '0 文字Static']
   |    |    | child_window(title="0 文字", auto_id="ContentTextBlock", control_type="Text")
   |    |    | 
   |    |    | Static - 'テキスト'    (L-31687, T-31159, R-31639, B-31127)
   |    |    | ['Static4', 'テキスト', 'テキストStatic']
   |    |    | child_window(title="テキスト", auto_id="ContentTextBlock", control_type="Text")
   |    |    | 
   |    |    | Static - ' 100%'    (L-31163, T-31151, R-31130, B-31135)
   |    |    | ['Static5', ' 100%Static', ' 100%']
   |    |    | child_window(title=" 100%", auto_id="ContentTextBlock", control_type="Text")
   |    |    | 
   |    |    | Static - ' Windows (CRLF)'    (L-31095, T-31151, R-31006, B-31135)
   |    |    | ['Static6', ' Windows (CRLF)', ' Windows (CRLF)Static']
   |    |    | child_window(title=" Windows (CRLF)", auto_id="ContentTextBlock", control_type="Text")
   |    |    | 
   |    |    | Static - ' UTF-8'    (L-30927, T-31151, R-30892, B-31135)
   |    |    | ['Static7', ' UTF-8Static', ' UTF-8']
   |    |    | child_window(title=" UTF-8", auto_id="ContentTextBlock", control_type="Text")
   | 
   | TitleBar - ''    (L0, T0, R0, B0)
   | ['TitleBar', ' UTF-8TitleBar']
   |    | 
   |    | Menu - 'システム'    (L-31992, T-31992, R-31970, B-31970)
   |    | ['システム', 'システムMenu', 'Menu2', 'システム0', 'システム1']
   |    | child_window(title="システム", auto_id="MenuBar", control_type="MenuBar")
   |    |    | 
   |    |    | MenuItem - 'システム'    (L-31992, T-31992, R-31970, B-31970)
   |    |    | ['MenuItem4', 'システム2', 'システムMenuItem']
   |    |    | child_window(title="システム", control_type="MenuItem")
   |    | 
   |    | Button - '元のサイズに戻す'    (L-31985, T-31999, R-31938, B-31977)
   |    | ['元のサイズに戻すButton', '元のサイズに戻す', 'Button15']
   |    | child_window(title="元のサイズに戻す", control_type="Button")
   |    | 
   |    | Button - '最大化'    (L-31938, T-31999, R-31892, B-31977)
   |    | ['最大化Button', '最大化', 'Button16']
   |    | child_window(title="最大化", control_type="Button")
   |    | 
   |    | Button - '閉じる'    (L-31892, T-31999, R-31845, B-31977)
   |    | ['閉じる', '閉じるButton', 'Button17']
   |    | child_window(title="閉じる", control_type="Button")

例ですが、ファイルタブについては以下の要素になります。

child_window(title="ファイル", auto_id="File", control_type="MenuItem")

編集タブについては以下の要素です。

child_window(title="編集", auto_id="Edit", control_type="MenuItem")

表示タブについては以下の要素になります。

child_window(title="表示", auto_id="View", control_type="MenuItem")

このように、操作を行いたい要素を、列挙した中から探していきます。

また、特定の要素をクリックした際に画面が変わる場合などは、再度要素を取得する必要があります。

メモ帳の例でいうと、右上の設定マークをクリックした場合、設定画面に遷移し、画面が変化します。

変化後の画面も自動化したい場合は、変化後の画面で再び要素を取得します。

しかし、要素を探すのは手間がかかります。

そこでマウスカーソルを移動させたりクリックしたりすることで容易に要素が分かるものが用意されています。

Accessibility Insights

非常に便利なので、ぜひインストールしてみてください。

③ 自動操作コードの作成

それぞれの要素に対してマウス操作やキーボード操作を与えるため、実際にコードを作成していきます。

クリック操作であれば.click_input()にて与えられます。

メモ帳の例でいうと、ファイルタブをクリックしたい場合は以下のようなコードになります。

Python
from pywinauto import Desktop

uia_window = Desktop(backend="uia")

notepad = uia_window.window(title_re=".*メモ帳")

# ファイルタブの要素を指定
file_tab = notepad.child_window(title="ファイル", auto_id="File", control_type="MenuItem")

# ファイルタブをクリック
file_tab.click_input()

右クリック操作であれば.right_click_input()にて与えられます。

例えばテキスト部分で右クリックしたい場合は以下のようなコードになります。

Python
from pywinauto import Desktop

uia_window = Desktop(backend="uia")

notepad = uia_window.window(title_re=".*メモ帳")

# テキスト部分の要素を指定
text_space = notepad.child_window(control_type="Document")

# 右クリック
text_space.right_click_input()

キーボード操作であれば.type_keys()にて与えられます。

例えばテキスト部分に特定の文字を入力し、Enterキーを押下したい場合は以下のようなコードになります。

Python
from pywinauto import Desktop

uia_window = Desktop(backend="uia")

notepad = uia_window.window(title_re=".*メモ帳")

# テキスト部分の要素を指定
text_space = notepad.child_window(control_type="Document")

# 文字入力
text_space.type_keys("テスト")

# Enterキー押下
text_space.type_keys("{ENTER}")

その他として、ダブルクリックしたい場合は.double_click_input()にて与えられます。

メモ帳に関する自動化について詳しく知りたい方は以下の記事をご覧ください。

あわせて読みたい
Windows11のメモ帳設定をPython(pywinauto)で完全自動化する方法 Windows11のメモ帳(Notepad)は、従来のメモ帳から大きく仕様が変更されました。デフォルトでは次のような設定になっています。 「文字列の折り返し」:オン 「ファイ...

自動化対象のPCにPython環境がない場合

特定のPCに対して自動化するだけでなく、複数のPCに対して自動化するパターンがあるかと思います。

でも「Pythonがないと動かないのでは?」と思うかもしれません。

実際、Pythonがなくてもpywinautoにおける自動化の処理を行うことができます。

方法としては.pyファイルをexe化することです。

exe化することで、どの環境においてもPythonを動かせるようになります。

exe化するには非常に簡単でpyinstallerというライブラリを使用します。

まずはライブラリをインストールしましょう。

▼ Terminalの場合 ▼

Python
pip install pyinstaller

▼ Jupyter Notebookの場合 ▼

Python
!pip install pyinstaller

インストール後、以下のコードで.pyファイルをexe化します。

Python
pyinstaller --onefile ".pyファイルのパス"

exe化されたものは「C:\Users\%username%\dist」配下に出力されています。

自動化動作を安定させる方法

pywinautoによる自動化は、百発百中ではなく、時々失敗します。

処理が先行しすぎて、画面が遷移する前に要素を取得してしまう場合もあります。

そうなると処理がそこで終了しやすくなってしまいます。

そうした場合を極力少なくするために、以下の処理を加えると良いです。

関数・メソッド・構文内容
.wait()操作可能な状態を待つ
.set_focus()指定した要素にフォーカスする
try / except例外処理
time.sleep()固定時間処理を待つ

これらを活用することで、安定した自動化を実現することができます。

以下が使用例になります。

Python
from pywinauto import Application, Desktop
from pywinauto.findwindows import ElementNotFoundError
import time

desktop = Desktop(backend="uia")
dlg = desktop.window(title_re=".*メモ帳")
dlg.set_focus()
time.sleep(1)

try:
    setting = dlg.child_window(title="設定", auto_id="SettingsButton", control_type="Button")
    setting.wait("exists enabled visible", timeout=5)
    setting.click_input()
    time.sleep(0.5)
except ElementNotFoundError:
    pass

# 文字列の折り返しをオフ
try:
    word = dlg.child_window(title="文字列の折り返し", auto_id="WordWrapSwitch", control_type="Button")
    word.wait("exists enabled visible", timeout=5)
    word.click_input()
    time.sleep(0.5)
except ElementNotFoundError:
    pass

まとめ

本記事では、BatやPowerShellだけでは自動化が難しいWindowsの画面操作を、Pythonのライブラリであるpywinautoを使って自動化する方法について解説しました。

pywinautoを使った画面操作の自動化は、以下の3つのステップで進められます。

  • pywinautoライブラリのインストール
  • 操作対象となる画面要素の特定
  • 自動操作コードの作成

pywinautoを活用すれば、Windowsアプリのボタンクリックや文字入力、設定変更など、これまで手作業で行っていた操作を自動化できる可能性があります。

BatやPowerShellによるコマンド操作と組み合わせることで、自動化できる作業の幅が広がり、作業時間の短縮や業務効率化にもつながります。

もちろん、すべての画面操作を自動化できるわけではありませんが、これまで自動化を諦めていた作業も、pywinautoを活用することで解決できるかもしれません。

ぜひ本記事で紹介したコードを参考に、実際にpywinautoを使いながら、今まで自動化できなかったWindowsの画面操作にも挑戦してみてください。


WinDeployAutoでは、Windowsのセットアップや定型業務の自動化・効率化をテーマに、Bat・PowerShell・Python などを活用した仕組みづくりを行っています。

  • Windowsセットアップを自動化したい
  • 繰り返し作業を効率化したい
  • 現場の運用負荷を減らしたい

このようなお悩みがありましたら、ぜひお問い合わせフォームよりお問い合わせください。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

システムエンジニアとしてWindows構築や業務自動化に携わってきました。
PowerShellやPython、AIを活用し、現場の課題を解決することが得意です。
「面倒な作業をなくしたい」「もっと効率化したい」そんな想いを形にします。

コメント

コメントする

目次