gologiusの巣

プログラミング、モデリングなどのメモです。誰かの役に立てるとうれしいです。

Selenium+Pythonにて、アラートが出る新規ウインドウに遷移したい

※Teratailで私が質問した問題を、結局自分で解決した際のメモです

Python - Selenium+Pythonにて、アラートが出る新規ウインドウに遷移したい(125969)|teratail

※2018/06/03追記

どうやらヘッドレスモード(GUI、ウインドウを表示しないモード)にすると、いちいち前のウインドウに遷移する必要がなくなるようです。 記事の最期の方に記載しておきます。

※2018/06/27追記 ↑とか書いちゃったのですが、実際には

  • 新規ウインドウが出るようなボタンをクリック
  • (新規ウインドウでアラートが出るまで待つ。)
  • switch_to_window()で新規ウインドウをアクティブにする

の流れで処理すると動くようです

やりたいこと

  • リンクをクリックする→新規ウインドウが開く(※ただしウインドウが開くと即アラートが出る)

という動きをSeleniumPythonで実現したいと考えています。

実際にはその先のボタンを押したいのですけどね。

下記のリンクで例を見ることが出来ます。

テストページ | gologiusのページ

問題点

調べているとどうも

  • アラートを消すためには、アラートが出ているウインドウに遷移しなければならない
  • ウインドウに遷移するためには、アラートを消さなければならない

と、デッドロックみたいな状況になっているようです。

以下ソースの★1、★2の部分です。

ソース

# -*- coding: utf-8 -*-
"""
Created on Wed Mar  7 22:16:01 2018

@author: 
"""

from selenium import webdriver
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException

import datetime
import traceback

###########################################################################
def waitOpenWindow(driver, beforeWinNum, maxWaitSec = 10):
    """
    指定された時間まで、ウインドウが開くのを待つ
    """

    beginTime = datetime.datetime.now()
    endTime = beginTime + datetime.timedelta(seconds=maxWaitSec)

    while(datetime.datetime.now() <= endTime):

        afterWinNum = len(driver.window_handles[-1])
        if afterWinNum == beforeWinNum:
            continue
        elif  afterWinNum > beforeWinNum:
            #ウインドウ数が増える=新規ウインドウが開いた
            return True , "指定時間内に新規ウインドウが開きました"
        else:
            return False, "ウインドウが外部から閉じられた可能性があります"

    return False, "指定時間内に新規ウインドウが開きませんでした"

###########################################################################
def waitAlert(driver, maxWaitSec = 10):
    """
    指定された時間まで、アラートが開くのを待つ    
    """

    try:
        wait = WebDriverWait(driver, maxWaitSec)
        wait.until(expected_conditions.alert_is_present())
    except TimeoutException as time_e:
        return 0, "アラート表示待機中にタイムアウトしました"
    except Exception as e:
        print(traceback.print_exc)
        return -1, "アラート表示待機中に予期しないエラーが発生しました"

    return 1, "アラートが表示されています : " + Alert(driver).text

###########################################################################
#関数定義
def access():

    try :
        print("begin process")

        driver = webdriver.Chrome("chromedriver_win32\chromedriver.exe")
        driver.implicitly_wait(10)
        driver.get("https://gologius.github.io/test.html")

        print("画面名", driver.title)
        winNum = len(driver.window_handles)

        #リンクをクリック
        driver.find_element_by_link_text("javascriptによる小ウインドウ表示").click()

        #ウインドウ表示待機     
        print("画面遷移待機中")
        waitResult, msg = waitOpenWindow(driver, winNum)
        if waitResult == False:
            return False, "ウインドウ表示に失敗しました"
        print(msg)

        #新規ウインドウに遷移する ★1
        win = driver.window_handles[-1] #リストの最後=最後に開いたウインドウ
        driver.switch_to.window(win)
        print("画面名", driver.title)
        winNum = len(driver.window_handles)

        #アラート待機 ★2
        print("アラート表示待機中")
        resultCode, msg = waitAlert(driver)
        if resultCode == 1:
            Alert(driver).accept()
        print(msg)

        print("end process")

    except:
        print("予期しないエラーが発生しました")
        print(traceback.format_exc())
        #ログ出力
        driver.quit()

    return True, "アクセス成功"

###########################################################################
#実行

result, msg = access()
print(msg)

解決方法

ボタンクリック後、「遷移前のウインドウ」にdriver.switch_to_window(win)で遷移すると、 その後のアラート処理、ウインドウ遷移が実行できるようです

win1 = driver.window_handles[-1]

driver.find_element_by_link_text("javascriptによる小ウインドウ表示").click()

#遷移前のウインドウに遷移する
driver.switch_to_window(win1)

#アラート処理

#ウインドウ遷移
win2 = driver.window_handles[-1]
driver.switch_to_window(win2)

理由は不明ですが、推測するに、新規ウインドウが表示される際に、

  • 新規ウインドウが開く際に、そのウインドウがアクティブになろうとする
  • Alertのせいでアクティブになりきれない →無限ループの原因
  • 遷移前のウインドウをアクティブに(switch_to_window)する
  • Alert(driver).accept()なども実行可能になる

みたいな気がします。


※2018/06/03追記↓

ヘッドレスモードで起動すると、この話は無視できる・・・?

        chromeOptions = webdriver.ChromeOptions()
        chromeOptions.add_argument('--headless') #ブラウザのGUIが表示されなくなる 
        driver = webdriver.Chrome("hogehoge/chromedriver.exe", chrome_options=chromeOptions)

雑感

上記のような不具合を抜きにしてもSelenium便利。

ちなみに検証用サイトはgithubioで作成しました。 テストページ | gologiusのページ

JavaScript使えるのでこういう際にサクッと作れるので便利。 ただJSの書き方を忘れていたのはナイショ...(/ω\)