3

我需要测试一个相当复杂的网页设置,其中包含嵌套框架。

在实际问题中,硒代码正在加载包含我想切换到的框架的新网页内容。为了避免任何显式等待,我尝试了以下代码片段:

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 能够在框架内找到框架并切换到它。看起来在错误情况下 selenium 切换到“frame1”,而“frame2”尚未加载,但“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”内。
4

2 回答 2

3

更好的方法是制作自己的 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。

于 2017-12-14T13:48:38.373 回答
1

此错误消息...

WebDriverException: Message: TypeError: can't access dead object

...表示在<iframes>.

有关以下方面的更多信息:

  • 相关的HTML
  • 框架集的存在
  • 的存在
  • 嵌套框架的层次结构
  • 帧加载顺序
  • 在各自的标签中存在JavaScriptAJAX 调用<iframe>

会帮助我们以更好的方式分析问题。然而,在这一点上值得一提的是,最初Selenium总是将焦点放在default_content上。以下是一些使用嵌套<frames>和的方法<framesets>

  • 如果frame1frame2都在同一级别,即在顶级浏览上下文下,您需要:

    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"))
    
  • 如果frame2嵌套在frame1中,要从frame1切换到frame2 ,您需要:

    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"))
    
  • 如果frame2frame3frame1内,那么要从frame2切换 到frame3,您需要:

    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"))
    
  • 如果frame2frame3在frame1内的frameset23内,那么要从frame2切换 到frame3,您需要:

    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 的更好方法

在处理时,您需要将WebDriverWaitexpected_conditions结合使用:

frame_to_be_available_and_switch_to_it()

例如,从顶级浏览上下文切换到<iframe>有效的代码行将是:

  • 使用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的方法

于 2017-12-14T13:36:40.910 回答