これに対する可能な解決策を探しているときに、SOなどでいくつかの解決策に出くわしました。それらの一部はAutoIT
、プロンプトなしでファイルを直接保存するために、ブラウザープロファイルを使用または編集していました。
ブラウザのプロファイルを編集することで [名前を付けて保存] ダイアログの問題を克服できるように、このソリューションはすべて具体的すぎることがわかりましたが、後で他のウィンドウを処理する必要がある場合は行き詰まります。使用するAutoIT
のはやり過ぎです。これは、私Python
がこのタスクを実行することを選択したという事実に直接衝突します。(Python
つまり、それ自体が非常に強力です。他のツールに応じて、Pythonistにとって厳密にNO NOです)
したがって、この問題に対する可能な一般的な解決策を長い間探した後、Selenium を使用して Web アプリケーションを自動化するプロセスで、「名前を付けて保存」、「ファイルのアップロード」などのネイティブ OS ダイアログボックスを処理しようとしている人だけでなく、 Web ドライバーだけでなく、 Python
APIのみを使用して特定のウィンドウとやり取りしたい人にも。
このソリューションは、のモジュールを利用Win32gui
しSendKeys
ますPython
。最初に、必要なウィンドウを取得するための一般的な方法を説明し、次に、Selenium Webdriver を使用して Web アプリケーションを自動化する際にこれを使用できるようにする小さなコードを追加します。
一般的な解決策::
import win32gui
import re
import SendKeys
class WindowFinder:
"""Class to find and make focus on a particular Native OS dialog/Window """
def __init__ (self):
self._handle = None
def find_window(self, class_name, window_name = None):
"""Pass a window class name & window name directly if known to get the window """
self._handle = win32gui.FindWindow(class_name, window_name)
def _window_enum_callback(self, hwnd, wildcard):
'''Call back func which checks each open window and matches the name of window using reg ex'''
if re.match(wildcard, str(win32gui.GetWindowText(hwnd))) != None:
self._handle = hwnd
def find_window_wildcard(self, wildcard):
""" This function takes a string as input and calls EnumWindows to enumerate through all open windows """
self._handle = None
win32gui.EnumWindows(self._window_enum_callback, wildcard)
def set_foreground(self):
"""Get the focus on the desired open window"""
win32gui.SetForegroundWindow(self._handle)
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
このコードを使用するには、取得するウィンドウの名前である文字列を指定する必要があります。この場合は「名前を付けて保存」です。同様に、任意の名前を指定して、そのウィンドウにフォーカスを当てることができます。目的のウィンドウのフォーカスを取得したら、SendKeys
モジュールを使用してキーストロークをウィンドウに送信できます。この場合、ファイルを保存する場所にファイルパスを送信し、ENTER
.
Selenium Webdriver に固有::
上記のコード セグメントを使用して、自動化中に Web アプリケーションを介してトリガーされるネイティブ OS ダイアログ ボックスを処理することができます。これSelenium Webdriver
には、コードを少し追加します。
このコードを使用しているときに直面する問題は、自動化コードがWeb Element
ネイティブ OS ダイアログ ウィンドウをトリガーするものをクリックすると、ネイティブ OS ダイアログ ウィンドウでのアクションを待っている時点でコントロールがスタックすることです。したがって、基本的にはこの時点で立ち往生しています。
回避策は、新しいthread
usingPython
threading
モジュールを生成し、それをクリックしてWeb Element
ネイティブ OS ダイアログ ボックスをトリガーすることです。親スレッドは、上記のコードを使用してウィンドウを見つけるために通常どおりに移動します。
#Assume that at this point you are on the page where you need to click on a Web Element to trigger native OS window/dialog box
def _action_on_trigger_element(_element):
_element.click()
trigger_element = driver.find_element_by_id('ID of the Web Element which triggers the window')
th = threading.Thread(target = _action_on_trigger_element, args = [trigger_element]) #Thread is created here to call private func to click on Save button
th.start() #Thread starts execution here
time.sleep(1) #Simple Thread Synchronization handle this case.
#Call WindowFinder Class
win = WindowFinder()
win.find_window_wildcard(".*Save As.*")
win.set_foreground()
path = "D:\\File.txt" #Path of the file you want to Save
ent = "{ENTER}" #Enter key stroke.
SendKeys.SendKeys(path) #Use SendKeys to send path string to Save As dialog
SendKeys.SendKeys(ent) #Use SendKeys to send ENTER key stroke to Save As dialog
#At this point the native OS window is interacted with and closed after passing a key stroke ENTER.
# Go forward with what ever your Automation code is doing after this point
ノート::
Web アプリケーションの自動化で上記のコードを使用する場合は、検索するウィンドウの名前を確認し、それを に渡しますfind_window_wildcard()
。ウィンドウの名前はブラウザに依存します。たとえば、要素をクリックしてファイルをアップロードするとトリガーされるウィンドウは、 では「ファイルのアップロード」と呼ばれFirefox
、 で開きChrome
ます。用途Python2.7
これが、一般的な形式で使用する場合でも、Web アプリケーションの自動化で使用する場合でも、同様のソリューションを探している人に役立つことを願っています.
編集:
コマンドライン引数を介してコードを実行しようとしている場合は、スレッドを使用してウィンドウを見つけ、Win32gui
元のプログラムスレッドを使用して要素をクリックしてみてください (ここではスレッドを使用してクリックされます)。その理由は、スレッドを使用して新しい接続を作成するときに、urllib ライブラリがエラーをスローするためです。)
参考文献::
そう質問
センキー
Win32gui