ネストされたフレームを含む、テストする必要があるかなり複雑な Web ページ設定があります。
実際の問題では、Selenium コードは、切り替えたいフレームを含む新しい Web ページ コンテンツをロードしています。明示的な待機を避けるために、次のコード スニペットを試してみました。
self.driver.switch_to_default_content()
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame1')))
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame2')))
ただし、このスニペットは常に失敗し、次のエラーが発生します。
...
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/wait.py", line 71, in until
value = method(self._driver)
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/expected_conditions.py", line 247, in __call__
self.frame_locator))
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/expected_conditions.py", line 402, in _find_element
raise e
WebDriverException: Message: TypeError: can't access dead object
ただし、さらにスリープを使用すると:
time.sleep(30)
self.driver.switch_to_default_content()
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame1')))
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame2')))
Selenium はフレーム内でフレームを見つけて、それに切り替えることができます。エラーの場合、「frame2」がまだロードされていないときに Selenium が「frame1」に切り替わりますが、「frame2」が「frame1」の他のインスタンスにロードされるか、Selenium によって認識されないように見えます (バグかもしれません?)。したがって、セレンは「frame1」内にあり、何らかの理由で「frame2」がロードされていることを認識していません。
これを (長いスリープを使用せずに) 解決できる唯一の方法は、次の醜いコードを使用することです。
mustend = time.time() + 300
while time.time() < mustend:
try:
self.driver.switch_to_default_content()
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
break
except WebDriverException as e:
self.log("Sleeping 1 sec")
time.sleep(1)
if time.time() > mustend:
raise TimeoutException
そのため、WebDriverException (デッドオブジェクト) が発生するたびに、トップレベルのフレームに移動し、フレームごとに内部フレームに切り替えようとします。
他に試せるアプローチはありますか?
追加情報
iframe はネストされています。つまり、「frame2」は「frame1」の中にあります。フレームを含む HTML を追加できますか?
– スーツ2017 年 12 月 14 日 11:26
@suit: それは不可能でしょう...
– アレックス2017 年 12 月 14 日 12:35
HTML の一部をコピーしてみますが、どうすれば簡単にコピーできるかわかりません...
– アレックス2017 年 12 月 14 日 15:14
ベットer のアプローチは、独自の Expected_condition を作成することです。 例:
class nested_frames_to_be_available_and_switch:
def __init__(self, *args):
"""
:param args: locators tuple of nested frames (BY.ID, "ID1"), (BY.ID, "ID2"), ...
"""
self.locators = args
def __call__(self, driver):
try:
for locator in self.locators:
driver.switch_to.frame(driver.find_element(*locator))
except WebDriverException:
driver.switch_to_default_content()
return False
return True
WebDriverWait(driver, 300).until(nested_frames_to_be_available_and_switch((By.ID, 'frame1'), (By.ID, 'frame1')))
しかし、おそらくその必要はないでしょう。それを伝えるには、HTML DOM を見る必要があります。
2
これは私のアプローチに似ていますが、一貫した名前でまとめられています。それは気に入っています...
– アレックス2017 年 12 月 14 日 14:26
@アレックス、私は撮りましたEC.* クラスをもう一度見てコードを修正しました
– スーツ2017 年 12 月 14 日 15:34
このエラー メッセージは...
WebDriverException: Message: TypeError: can't access dead object
...<iframe> 間の切り替え中にエラーが発生したことを意味します。
以下に関する詳細情報:
関連する HTML フレームセットの存在 フレームの存在 ネストされたフレームの階層 フレームロードのシーケンス それぞれの <iframe> 内の JavaScript および AJAX 呼び出しの存在タグもっと良い方法で問題を分析するのに役立つはずです。ただし、この時点で、最初は Selenium が常にdefault_cにフォーカスを置くことを言及する価値があります。内容。ここでは、ネストされた <frames> を操作するためのいくつかのアプローチを示します。および <フレームセット>:
フレーム 1 とフレーム 2 の両方が同じレベル、つまり最上位のブラウジング コンテキストの下にある場合、次の操作を行う必要があります。
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
self.driver.switch_to_default_content()
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
フレーム 2 がフレーム 1 内にネストされている場合、フレーム 1 からフレーム 2 に切り替えるには、次の操作を行う必要があります。
self.driver.switch_to_default_content()
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
//code
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
フレーム 2 とフレーム 3 がフレーム 1 内にある場合、フレーム 2 からフレーム 3 に切り替えるには、次の操作を行う必要があります。
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
フレーム 2 とフレーム 3 がフレーム 1 内のフレームセット 23 内にある場合、フレーム 2 からフレーム 3 に切り替えるには、次の操作を行う必要があります。
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
#ignore presence of frameset23
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
#ignore presence of frameset23
self.driver.switch_to.frame(self.driver.find_element_by_id("frame3"))
適切な WebDriverWait を使用したより良いアプローチ
iframe と Frameset を扱うときは、expected_conditions と組み合わせて WebDriverWait を誘導する必要があります。
frame_to_be_available_and_switch_to_it()
トップレベルのブラウジングコンテキストから<iframに切り替える例として例>有効なコード行は次のようになります。
フレーム ID の使用:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"frameID")))
フレーム名を使用しています:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.NAME,"frameNAME")))
フレーム CLASS_NAME を使用しています:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.frame_CLASS_NAME,"u_0_f")))
フレーム CSS_SELECTOR の使用:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"frame_CSS_SELECTOR")))
フレーム XPATH の使用:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"frame_XPATH")))
TL;博士
iframe で #document を処理する方法
2
WebDriverWait を含むすべてを試しました。これにより、同じエラーが発生します (つまり、WebDriverWait を使用する質問を参照してください...)
– アレックス2017 年 12 月 14 日 15:23
DebanjanB: HTML DOM、フレームセット、フレームの読み込み、JavaScript との対話、AJAX 呼び出しなどについて詳しく説明したドキュメントや本をご存知ですか?
– アレックス2018 年 7 月 5 日 9:51