現在VB.NetとSeleniumを組み合わせて何か新しい事を出来ないかと試行錯誤していますが、その中でちょっと手間取ったウィンドウの切り替え方法について記録を残しておきます。
まずやろうとしていた内容は以下の通り。
1.Chrome操作中にURLを指定して新規タブを追加
2.そのタブにフォーカスを移動させて色々と操作
3.元居たタブにフォーカスを戻す
順に解説していきます。
- 目次 -
URLを指定して新規タブを追加
まずタブの追加方法についてはWebDriverからではなく、Javascriptを実行させてブラウザ側で追加する様にします。
※そもそもWebDriverでタブを追加させる方法が分からない
WebDriverからJavascriptを実行させるには「ExecuteScript」を実行してやれば良いので、Javascriptで新規ウィンドウやタブを開くための「window.open()」と組み合わせて、
chromeDriver.ExecuteScript("window.open('https://www.microsoft.com/ja-jp')")
という感じに記述します。
上の例ではopenの引数に日本マイクロソフトのURLを指定していますが、ここは以下の様に空欄でもOK。
chromeDriver.ExecuteScript("window.open()")
この場合はbrankページが起動するので、ここからページ移動するなり自由に操作可能です。
新規タブにフォーカスを移動させる
続いて、先ほど追加したタブに操作を移します。
というのも、タブを追加した時点で見た目上は今開いたタブが表示されているのですが、内部的には処理実行時に表示されていた元のページを操作している状態のままとなっているからです。
以下の画像をご覧下さい。
処理実行時に開いていたページは「Google」です。
その後、前述のwindow.openを使用してマイクロソフトのページを新規タブで追加した形となりますが、操作しているドライバーオブジェクトの現在のページタイトル名を表示させてみると「Google」となっています。
という事でこのままでは新規タブを操作する事ができないので、処理を追加して操作権を移す必要があります。
さて、とは言ったもののどうすれば良いんだと調べてみると・・・VBのサンプルは全然見付かりませんでしたが、JavaやらPythonの解説は大量に出てきたのでそちらを参考にしてみる事に。
見た所、Javaでは以下の様に「switchTo().window()」でウィンドウハンドルを指定できるみたいですね。
//元のWindowHandleを格納 String wHandle = driver.getWindowHandle(); //別Window切り替え driver.switchTo().window("hoge"); //元のWindowへ切り替え driver.switchTo().window(wHandle);
じゃあ同じ感じでVBでもswitchToでハンドルを指定してみようと思ったのですが・・・
・SwitchToAlert
・SwitchToDefaultContent
・SwitchToFrame
・SwitchToNextWindow
・SwitchToParentFrame
・SwitchToPreviewsWindow
・SwitchToWindowByName
・SwitchToWindowByTitle
・・・あれ?ByHandle(的なもの)は?
暫く探してみましたがありませんでした \(^o^)/
なぜだーーーー!!!
・・・ま、まぁでも他に幾つかウィンドウ切り替えメソッドはあるみたいだし、そっちで実装してみるか。
とりあえずそれぞれの動きを検索してみよう。
・・・と思っても全然ヒットしねぇ!(英語サイトばっかりだしヒット数二桁)
もしかして何かやり方間違ってる?
それともVB(笑)でSeleniumはもうやってる人皆無とかそういう事?
理由は分かりませんが、参考に出来るものが無い以上自分で試してみるしかないという事で、幾つか動きを確認してみました。
SwitchToAlert
これは試していません。
が、名前から察するにページ上に表示されたアラートウィンドウを検知してフォーカスするって事かな。
SwitchToNextWindow
タブを新規追加した状態でこれを実行すると、新規追加されたタブにフォーカスが移動します。更に実行すると、一周回っても最初のタブにフォーカスが移動します。
例として以下の状態のブラウザに対して、
「SwitchToNextWindow」を実行し続け、実行後のページタイトルとハンドルをそれぞれ表示する処理を実行してみると・・・
最初と最後のGoogleはハンドルの値が一緒なので、フォーカスが一周しているのが分かりますね。
※Googleが初期ページ、無題が追加ページ、後の3つは前回のセッションの復元で最初から存在したページ
SwitchToPreviewsWindow
これは「SwitchToNextWindow」の逆バージョンですね。
「SwitchToNextWindow」が1→2→3→4→5と巡るのに対し、
こちらは5→4→3→2→1という順番で動作します。
SwitchToWindowByTitle
これは「ByTitle」の通りページのタイトル名を指定してフォーカスを移す処理です。
注意点としては完全一致で無いと以下の様なエラーが出ます。
※「Google」は開いていましたが「Goo」だけでは検知されず。
また、同じタイトル名のページが複数ある場合は「先に見付かった方」となるので、場合によっては使えません。
他はまだ試していません。
「SwitchToFrame」とか何に使うんだろう・・・
フレームって「<frame></frame>」このフレーム?いや違うよなぁ・・・
SwitchToWindowByHandleを自分で用意する
という訳で幾つか仕様を確認してみましたがどれも確実性に欠けるので、他の言語では用意されているHandle指定バージョンを自分で用意する事にしました。
処理はざっくりこんな感じ。
''' <summary> ''' 指定したハンドルのウィンドウにフォーカスを切り替える ''' 切り替え成功でTrue、対象のウィンドウが見付からなければFalse ''' </summary> ''' <param name="handle"></param> ''' <returns></returns> Public Function SwitchToHandle(handle As String) As Boolean Dim firstHandle As String = "" Dim tmpHandle As String = chromeDriver.Window.Handle ' ハンドルが一致するまでタブを切り替えて比較 Do While firstHandle <> tmpHandle chromeDriver.SwitchToNextWindow() If firstHandle = "" Then firstHandle = tmpHandle tmpHandle = chromeDriver.Window.Handle Loop Return firstHandle <> tmpHandle End Function
SwitchToNextWindowで全タブをループさせつつハンドルの値で比較しています。最終的に見付かればTrueを返す仕様。
デメリットはSwitchToNextWindowを使用した際のタブ移動に数秒程度時間が掛かるため、タブをたくさん開いている場合は探すのに少し時間が掛かってしまう所。
そしてもう一つ。
「新しく追加したタブのハンドルをどうやって取得するか?」という所なんですが、タブを追加した後に
chromeDriver.SwitchToNextWindow().Handle
を記述して、次のウィンドウのハンドルを取得するという形にしています。
ただしこの手順には一つ問題があり、
chromeDriver.ExecuteScript("window.open()")
を実行してタブを追加した場合は、少し時間を空けないと新規追加したタブを認識してくれない場合が有ります。(URLを指定していても同じ)
理由は単純で、WebDriverは「Javascriptを実行させる」という命令を『出すだけ』で、その結果については待たずに次の処理へ移ってしまうからです。
Javascriptが結果を返すのはサイトやブラウザに対してであって、WebDriverにでは無いですからね。こればっかりはどうしようもない。
この辺は安定性に欠けるので、今後直していかないといけない所です。
という所で今回はここまでです。
ではまた。