閉じる

Seleniumで複数タブ・ウィンドウを自在に操る!window_handlesを使った切り替え方法

Webブラウザの自動化において、リンクをクリックした際に新しいタブやウィンドウが開く挙動は非常に一般的です。

Seleniumを使用してブラウザ操作を自動化する場合、単に新しいタブが開いただけでは、操作対象(フォーカス)は自動的には移動しません。

プログラム側で明示的に操作対象を切り替える処理を記述しなければ、スクリプトは元のウィンドウで要素を探し続け、エラーを発生させてしまいます。

本記事では、Seleniumにおける「ウィンドウハンドル」の概念から、最新のPython環境でタブやウィンドウを自在に切り替える方法について詳しく解説します。

ウィンドウハンドルとは何か

ウィンドウハンドルとは、ブラウザが個々のタブやウィンドウを識別するために割り当てる一意の識別子のことです。

Seleniumはこのハンドルを利用して、現在どの画面を操作しているのかを管理しています。

新しいタブが開いたとしても、Seleniumの「WebDriverインスタンス」は依然として古いハンドルを指したままの状態になります。

そのため、新しく開いたページ上の要素を操作したい場合は、適切なハンドルを取得して切り替えを行う必要があります。

このハンドルはランダムな文字列のような形式で生成されるため、人間が直接指定するのではなく、Seleniumのメソッドを介して取得するのが一般的です。

ウィンドウハンドルを取得するためのプロパティ

Seleniumには、ウィンドウハンドルを扱うための主要なプロパティが2つ用意されています。

1つ目は、現在アクティブなウィンドウのハンドルを返すdriver.current_window_handleです。

2つ目は、現在ブラウザで開いているすべてのタブやウィンドウのハンドルをリスト形式で返すdriver.window_handlesです。

基本的には、この2つのプロパティを組み合わせることで、目的のタブへの移動を実現します。

プロパティ名取得内容
current_window_handle現在操作対象となっている単一のハンドルを取得します。
window_handles現在開いているすべてのタブのハンドルをリスト形式で取得します。

基本的なタブ切り替えの実装方法

実際に、リンクをクリックして新しいタブが開いた後に、そのタブへ操作を移す基本的な流れを確認しましょう。

まずは、新しいタブが開く前のハンドルを記録しておき、クリック後に増えたハンドルを特定する方法が確実です。

Python
from selenium import webdriver
from selenium.webdriver.common.by import By

# WebDriverのセットアップ
driver = webdriver.Chrome()

# 1. 最初のページにアクセス
driver.get("https://example.com")

# 2. 現在のウィンドウハンドルを保存
main_handle = driver.current_window_handle
print(f"元のハンドル: {main_handle}")

# 3. 新しいタブを開くリンクをクリック
driver.find_element(By.LINK_TEXT, "詳細を見る").click()

# 4. 全てのウィンドウハンドルを取得
all_handles = driver.window_handles

# 5. 新しいタブのハンドルに切り替え
for handle in all_handles:
    if handle != main_handle:
        driver.switch_to.window(handle)
        break

# 6. 新しいタブで処理を行う
print(f"現在のハンドル: {driver.current_window_handle}")
print(f"新しいタブのタイトル: {driver.title}")

# ブラウザを閉じる
driver.quit()
実行結果
元のハンドル: CDwindow-XXXXXXXXXXXXXXXXXXXX
現在のハンドル: CDwindow-YYYYYYYYYYYYYYYYYYYY
新しいタブのタイトル: 詳細ページ

上記のコードでは、forループを使用して元のハンドル以外のものを探し、driver.switch_to.window(handle)で切り替えを実行しています。

これがSeleniumにおけるウィンドウ切り替えの最も基本的な形となります。

Selenium 4以降で導入された新しいウィンドウ操作

Selenium 4からは、既存のリンクをクリックするだけでなく、明示的に新しいタブやウィンドウを作成して切り替えるための便利なメソッドが追加されました。

driver.switch_to.new_window('tab')を使用すると、新しく空のタブが作成され、操作対象も自動的にその新しいタブへ移動します。

また、引数に'window'を指定すれば、新しいウィンドウを立ち上げることも可能です。

この機能により、従来のようにJavaScriptを実行したりショートカットキーを送信したりする手間が省けるようになりました。

Python
# Selenium 4以降の新しいタブ作成機能
driver.get("https://google.com")

# 新しいタブを作成して切り替える
driver.switch_to.new_window('tab')

# 新しいタブで別のURLを開く
driver.get("https://bing.com")

print(f"現在のページタイトル: {driver.title}")
実行結果
現在のページタイトル: Bing

このように、新しく別のサイトを並行して開きたい場合は、このnew_windowメソッドを活用するのがスマートです。

タブを閉じて元のページに戻る処理

複数のタブを操作する場合、特定の処理が終わった後にそのタブを閉じ、元のページに制御を戻したいケースが多々あります。

タブを閉じるにはdriver.close()を使用しますが、注意が必要なのは、タブを閉じても操作対象は自動的に戻らないという点です。

たとえアクティブだったタブが消滅しても、プログラム上の「ターゲット」は存在しない場所を指したままになります。

したがって、close()を呼んだ直後には、必ずswitch_to.window()を実行して有効なハンドルに切り替える必要があります。

Python
# 現在のタブを閉じる
driver.close()

# 明示的に元のハンドルに戻る
driver.switch_to.window(main_handle)

print(f"戻り先のタイトル: {driver.title}")

この手順を忘れると、次に要素を操作しようとした際にNoSuchWindowExceptionが発生してしまいます。

待機処理(Explicit Wait)を活用した安定化

Webアプリケーションの挙動によっては、リンクをクリックしてから新しいハンドルが生成されるまでに数秒のラグが生じることがあります。

ハンドルがまだ存在しない状態でwindow_handlesを取得しようとすると、期待した結果が得られない場合があります。

このような不安定さを解消するためには、WebDriverWaitを利用して、ウィンドウの数が増えるまで待機する処理を入れるのがベストプラクティスです。

Python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# ウィンドウの数が2つになるまで最大10秒待機
WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2))

# 待機後にハンドルを取得して切り替え
new_handle = [h for h in driver.window_handles if h != main_handle][0]
driver.switch_to.window(new_handle)

expected_conditions.number_of_windows_to_be(n)を使用することで、ネットワークの遅延やレンダリングの遅れによるエラーを大幅に減らすことができます。

自動化の信頼性を高めるために、「新しいウィンドウが開くはず」という箇所には必ず待機処理を入れるようにしましょう。

多数のタブを管理するためのテクニック

3つ以上のタブを同時に開いて操作する場合、インデックス番号(driver.window_handles[2]など)で指定するのは避けるべきです。

タブが開く順番はブラウザの内部処理により前後する可能性があり、インデックスによる指定はバグの原因になりやすいからです。

確実な方法として、「各ハンドルのページタイトルやURLを確認して目的のページを特定する」というロジックを推奨します。

Python
def switch_to_tab_by_title(target_title):
    for handle in driver.window_handles:
        driver.switch_to.window(handle)
        if target_title in driver.title:
            return True
    return False

# 目的のタイトルを持つタブへ切り替え
if switch_to_tab_by_title("マイページ"):
    print("ターゲットのタブに移動しました")
else:
    print("目的のタブが見つかりませんでした")

このように関数化しておくことで、複数のウィンドウが混在する複雑な自動化シナリオでも、迷うことなく正確な操作が可能になります。

よくあるトラブルと解決策

ウィンドウ切り替えにおいて頻出するエラーの1つに、ポップアップウィンドウの扱いや、広告によって意図しないウィンドウが開いてしまうケースがあります。

意図しないウィンドウが開いた場合は、window_handlesの要素数が予想より多くなります。

その場合は、前述のタイトル判定やURL判定を組み合わせて、操作すべき「正しいハンドル」を動的に特定するように設計してください。

また、driver.quit()はすべてのウィンドウを閉じてセッションを終了させますが、一部のタブだけを残して作業を続けたい場合は必ずdriver.close()を使い分けるようにしましょう。

さらに、<iframe></iframe>(インラインフレーム)内の操作と、ウィンドウの切り替えを混同しないように注意が必要です。

ウィンドウの切り替えはdriver.switch_to.window()ですが、フレームの切り替えはdriver.switch_to.frame()を使用します。

画面が変わったように見えても、それが新しいウィンドウなのか、現在のページ内のフレームなのかを見極めることが重要です。

まとめ

Seleniumでの複数タブ・ウィンドウの操作は、ウィンドウハンドルの仕組みを理解すれば決して難しくありません。

window_handlesで識別子の一覧を取得し、switch_to.window()で切り替えるという一連の流れが基本となります。

最新のSelenium 4であれば、new_windowメソッドを活用することで、より直感的に新しい画面を生成できます。

実際の運用では、ネットワークの遅延を考慮したWebDriverWaitによる待機や、タイトルによる確実なタブ特定ロジックを組み込むことが、安定したスクリプト作成の鍵となります。

今回紹介したテクニックを駆使して、複雑なWebアプリケーションの操作を自由自在に自動化していきましょう。

URLをコピーしました!