はじめに
前回、Seleniumで職場のルーティンを自動化した記事を書いた
しかし実行中にエラーが発生することが何度かあったので、その原因と解決作を記録する
前回↓
原因
それはウィンドウの切り替えである。
PDFの添付後、別ウィンドウがポップアップし、「OK」ボタンのクリックを要される。クリックをすると当該ウィンドウは自動で消える。これをSeleniumで動かしてみると以下の流れになる。
1.ポップアップされたウィンドウに切り換える 2.「OK」ボタンのidに対して`.click()`若しくは`.send_keys(Keys.ENTER)`を実行 3.ポップアップされたウィンドウが自動的に消えるので、元々開いていたウィンドウに切り換える
上記操作をSeleniumで叶えるためには、windowhandleを知る必要がある。
windowhandleとは?
ウィンドウを識別するための情報
複数のウィンドウを操作したいとき、windowhandleを使うことでSeleniumの操作対象を切り替えることができる。
- windowhandleを取得する方法
①. 現在操作対象であるウィンドウのwindowhandleを取得
handle = driver.current_window_handle
②. 全ウィンドウ(指定したインスタンス)のwindowhandleを取得(リスト)
handles = driver.window_handles
- ウィンドウを切り替える方法
driver.switch_to_window(handles[2])
又、window_handles
の取得順は以下のとおりとなるらしい
index0 : 1(一番最初のウィンドウ)
index1 : 3(最新のウィンドウ)
index2 : 2(最新より1つ前のウィンドウ)
一番最初のウィンドウは0
に、それ以降は新しく開いたウィンドウから割り振られていく
以上がwindowhandle
の概要である。
これを用いてポップアップに対してSeleniumを実行すると、体感的に30%程の確立で「OK」のクリックに失敗する。
エラーコードを見てみると、「OK」のidが見つからなかったと書いてある。しかし見つかるときもある。一体なぜなのか...原因を探してみたら次のとおりだった。
原因
window_handlesの取得順がたま〜にちぐはぐになってしまっていた
(逐次取得したwindow_handlesをprintしてみたら判明した)
index0 : 3
index1 : 1
index2 : 2
や
index0 : 1
index1 : 2
index2 : 3
など。順番が前述の通りにならないケースがあり、理由はわからなかった...
しかし、try - except
を用いることでこの問題をクリアすることができた
解決策
from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException wait.until(lambda d: len(driver.window_handles) > 2) handle = driver.window_handles driver.switch_to_window(handle[1]) try: driver.find_element_by_id(b1).send_keys(Keys.ENTER) except NoSuchElementException: try: driver.switch_to_window(handle[2]) driver.find_element_by_id(b1).send_keys(Keys.ENTER) except NoSuchElementException: driver.switch_to_window(handle[0]) driver.find_element_by_id(b1).send_keys(Keys.ENTER)
当インスタンスを用いて出現するウィンドウは全部で3つである。
1. ポップアップのウィンドウは最新 = index1に格納されるはずなので、handle[1]
にスイッチ
2. その後「OK」のidを格納している変数b1
に対してENTER
を実行、実行されれば問題なし
3. idb1
が見つからない場合、NoSuchElementException
エラーが発生するためexcept
で救出してあげる
なお、except
を用いる時は以下のとおり例外クラスをimport
してあげる必要がある
from selenium.common.exceptions import [TheNameOfTheExceptionClass]
7. WebDriver API — Selenium Python Bindings 2 documentation
4. except
内で更にtry
してhandle[2]
のウィンドウにスイッチ
5. idb1
に対してENTER
、実行されれば問題なし
6. idb1
が見つからない場合、NoSuchElementException
エラーが発生するのでexcept
で救出。
7. 最後のウィンドウhandle[0]
にスイッチしてidb1
に対してENTER
を実行
8. 以上
上記であれば全windowhandleを捜索するので漏れはない。
結果
エラーが出ることはなくなりました