技術は使ってなんぼ

自分が得たものを誰かの役に立てたい

【Selenium】frameやiframeの取得が安定しない時の対処法


こんにちは。

Seleniumで自動操作をする際、要素が上手く取得できない時ってありますよね。

そういう場合、主にframeやiframeが原因だったりします。

しかしframeやiframeが上手く取得できるようにプログラムを組んだのに、何度か実行すると上手く要素が見つけられないことがありました。

今回はそんな時にうまくいった対処法を紹介しますので、良かったら参考にしてください。

1.まずはsleepを挟んでみよう!

まずは要素が正しく取得できない原因として、そもそもブラウザの情報が全て立ち上がっていないことが挙げられます。

要素が取得できない箇所の直前に、timeモジュールのsleep(3)等と少し長めに時間をとってみましょう。

ここはPC環境にもよるところがあるので、ある程度長くとって動作が安定するのであればこの方法で解決するかと思います。

私の感覚として、ブラウザが完全に開いてるのに5秒以上sleepさせても安定しない場合、この方法は使えないと思います。

その場合は次の2番で確認しましょう。

2.ただしくhtmlは取得できているか確認してみよう!

要素でエラーが出る場合、そもそもwebdriverは正しくブラウザを選択できているのかを確認する必要があります。

正しくブラウザを選択できていなければ、そりゃ要素は見つからないですよね。

エラーが出る直前で、driver.page_sourceとしてhtmlを確認してみましょう。

おそらく長くなることが予測されるので、driver.page_sourceをテキストファイル等に保存することをおすすめします。

保存したテキストファイルで、うまくいく時のhtmlと上手くいかない時のhtmlで比較してください。

frameやiframe等の場合、正しくブラウザのハンドルが握れていないことが確認できると思います。
(今回の私のハマったケースはまさにこれでした。。)

ではなぜそんなことが起こるのか、次の3で解説します。

3.windows_handlesは必ずしも順番通りに格納されているわけではない!

新しく開いたブラウザのハンドルを握りたいとき、こんな書き方してませんか?

handles = driver.window_handles

driver.switch_to.window(handles[-1])

このコード、他の技術ブログでも見かけるし、私も参考にしたのですが、実は間違いです。

というのも、handlesのリストは必ずしも開いたブラウザの順番に格納されるわけではないからです。

これはPythonのリストでもよくある話ですが、格納される順番はランダムです。

だからhandles[-1]としても、最新の開いたブラウザを操作できるという保証はありません。

ちなみに前章で確認したhtmlの中身を見ると、他のブラウザを指定していることも確認できます。

ではどうするか。

私はこんな方法で安定化をはかってみました。

handles = driver.window_handles
flg = False
for i in range(len(handles)):
    if flg:
        break
    try:
        driver.switch_to.window(handles[i])
        iframe = driver.find_element(By.XPATH, 'iframe‗xpath')
        driver.switch_to.frame(iframe)
        flg = True
    except:
        pass

シンプルにhandlesを全てチェックさせます。笑

後はtryを使って、要素が見つからないエラーを検出するならそのハンドルをスルーして、次のハンドルで確認させるという寸法です。

また正しくウインドウを選択できた場合、for文から抜けられるようにbreakできる仕組みも用意しました。

私はこれで安定してframeやiframeの遷移ができるようになりました。

Seleniumの開発で安定しないことでお困りの方のお役に立てれば幸いです。