【问题标题】:iframe element missing from seleniumselenium 中缺少 iframe 元素
【发布时间】:2021-11-19 13:04:25
【问题描述】:

我正在尝试使用 python selenium 执行一些自动化功能,但遇到了一些奇怪的行为。

html的总体布局:

<html>
  <body>
    <div class="parent">
      <iframe style="display: none"> ... </iframe>
      <iframe style="display: none"> ... </iframe>
      <iframe style="display: block">
        #document
        ...
        <div class="someClass"> ... </div>
      </iframe>
      <iframe style="display: none"> ... </iframe>
      <iframe style="display: none"> ... </iframe>
    </div>
  </body>

现在,每个 iframe 实际上都有相同的内部 html,并且来自网站的代码似乎是随机选择哪个 iframe 获取display="block"。但是,我找不到任何 iframe。

我尝试了一种标准方式:iframe = driver.find_elements_by_xpath("//iframe[contains(@style, 'display:block')]")

失败了,然后我试图找到任何 iframe:driver.find_element_by_tag_name("iframe")

它们都没有找到任何 iframe 元素。我看到以下错误:

Traceback (most recent call last):
  File "myfile.py", line 60, in <module>
    iframe = driver.find_element_by_xpath("//iframe[contains(@style, 'display: block')]")
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 394, in find_element_by_xpath
    return self.find_element(by=By.XPATH, value=xpath)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 978, in find_element
    'value': value})['value']
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidSelectorException: Message: invalid selector: The result of the xpath expression "//iframe[contains(@style, 'display: block')]" is: [object HTMLIFrameElement]. It should be an element.
  (Session info: chrome=93.0.4577.63)

关于为什么 xpath 返回 [object HTMLIFrameElement] 以及为什么我在通过 xpath 搜索时无法像访问其他对象一样访问它有什么想法吗?

编辑

新代码选项 1:

iframes = driver.find_elements_by_xpath(".//iframe[contains(@style,'display: block')]")

这仍然会引发与上面完全相同的错误

新代码选项 2:

parent = driver.find_element_by_xpath("//div[@class='parent']")
iframes = parent.find_elements_by_tag_name("iframe")
// when I print typeof iframes here, it's a list of dicts
// find the right index. Here, for simplicity, I just set it a default value
index = 4
// ...
driver.switch_to.frame(iframes[index])

我收到以下错误:

Traceback (most recent call last):
  File "myfile.py", line 76, in <module>
    driver.switch_to.frame(iframe)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\switch_to.py", line 89, in frame
    self._driver.execute(Command.SWITCH_TO_FRAME, {'id': frame_reference})
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: missing 'ELEMENT'
  (Session info: chrome=93.0.4577.82)

当我打印出 iframe 时:

[{}, {}, {}, {}, {u'ontouchmove': {}, u'ontouchstart': {}}, {}, {}, {}, {}, {}]

作为参考,这是我要访问的页面。有时您必须刷新几次才能获得挑战,而使用 selenium 时会更频繁。此外,使用无头模式会导致每次都发生挑战...https://catalog.usmint.gov/coins/coin-programs/morgan-and-peace-silver-dollar-coins/

【问题讨论】:

  • 这能回答你的问题吗? Switch to an iframe through Selenium and python
  • 没有。正如我在问题中提到的那样,我什至在尝试切换到它之前都找不到该元素
  • 您是否已在 selenium 中导出了 html 源代码,以确保这些元素存在以供手动检查/搜索?与在完整桌面浏览器中呈现的方式相比,目标网页可能不会像您期望的那样通过模拟浏览器呈现。我记得在过去广泛追查类似的问题只是发现 webdriver 浏览器呈现完全不同的内容与完整的 chrome 浏览器。
  • 我确实尝试导出 html,但它正在动态呈现。在导出的 html 中,我确实看到了一些 iframe 元素,但它们都没有子元素。这与我在查看开发人员选项时看到的不同,但我认为如果它是动态渲染的,我希望看到不同,对吧?

标签: python selenium selenium-webdriver iframe captcha


【解决方案1】:

我见过的唯一情况是,当我试图从任何类似的广告中访问 iframe 时,在我试图获取它们的那一刻就消失了。

我总是用getElementsByTagName("iframe") 解决这个问题,所以在加载之前尝试再等待页面加载,以确保 iframe 在运行之前完全初始化。 一种方法已在this question

上讨论过

另外,这里是关于 python 等待模式的官方文档: https://selenium-python.readthedocs.io/waits.html

PS:刚刚测试了您的示例 html 页面,当使用 document.getElementsByTagName("iframe") 时,我可以轻松地在浏览器上获取它们,如下图所示,所以很可能您遇到了我遇到的问题之一如上所述,因为您的 Selenium 应该能够看到它们,假设它们是静态的并且不会消失并且您的页面已完全加载:

额外细节

在您的情况下,如果您收到的属性是 HTMLIFrameElement 而不是简单的 iframe 标签,这意味着您正在处理一个 Web 界面,您可以从中直接访问它们的属性,这意味着确实你在页面上找到了一个 iframe。 您可以使用它的属性来访问native APIs,它有一个.src 属性,反映了它正在加载的URL,并且在许多情况下,您可以在不同的页面中打开这个URL,并获取它所呈现的内容直接(除非 URL 包含一些 CORS 块)。 此外,在 Selenium 上确实存在一些与 WaitForPageToLoad 相关的错误,这可以使用其他方法修复,例如 described here,尽管我认为这不是您当前的问题。

【讨论】:

  • 我绝对认为该网站正在做某事。这是一种验证码页面,所以我认为他们以某种方式欺骗 selenium 使其无法看到屏幕呈现的内容。
  • 我的输出 ([object HTMLIFrameElement]) 是未找到元素时的标准输出,还是我以某种方式抓取了错误级别的对象?
  • @lcta0717,我刚刚更新了我的答案以包含与HTMLIFrameElement 相关的要点。请看看是否有帮助。
  • 我将如何访问该元素? selenium return 抛出错误,因为它没有返回元素
  • @lcta0717 但是你提到你可以访问object HTMLIFrameElement,所以你得到了从 web apis 返回的 iFrame 元素,作为我添加详细信息的链接。问题可能与您的 xpath 表达式有关。尝试运行driver.find_elements_by_tag_name("iframe"),但请注意elements 的复数形式。因为如果此页面有多个iframes,您可能会得到一个在页面加载后消失的引用或任何相关问题,这可能会阻止您访问它。
【解决方案2】:

当帧需要时间渲染并且最初未被代码捕获时,我遇到过这种情况。以下方法对我有用。

iframes = driver.find_elements_by_tag_name('iframe')

for iframe in iframes:
if 'block' in iframe.get_attribute('style'):
    driver.switch_to.frame(iframe)
    break

【讨论】:

    【解决方案3】:

    ¿您是否尝试过 Selenium 文档的示例?

    See cap. "Using an Index"

    # switching to second iframe based on index
    iframe = driver.find_elements_by_tag_name('iframe')[1]
    
    # switch to selected iframe
    driver.switch_to.frame(iframe)
    

    【讨论】:

    • 不幸的是,这不起作用。虽然我可以在父级的 innerHTML 中看到 iframe,但使用上述任何一种方法都找不到 iframe
    【解决方案4】:

    每当我遇到 iframe 时,我大部分时间都无法直接访问它们。相反,我必须将 xpath 更改为 iframe 其父元素并从那里访问它,如下所示。

    try:
        # instead of xpath = '//iframe' try accessing the parent element and add /iframe
        iframe_xpath = '//*[@id="parent-element"]/iframe'
        
        # or more specifically to get the right one in your case
        iframe_xpath = '//*[@id="parent-element"]/iframe[contains(@style, "display: block")]'
    
        WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, iframe_xpath)))
                  
    
        driver.switch_to.frame(driver.find_element(By.XPATH, iframe_xpath))
        # Do your thing in the frame
        ...
    
        # go back to the normal content
        driver.switch_to.default_content()
    except:
        # Ideally you catch each error separately; TimeoutException, NoSuchElementException, ...
        pass
    

    【讨论】:

    • 不幸的是,这不起作用。事实上,父母表明它没有孩子
    • 在您的问题中,您有一个示例 html,其中父元素甚至有自己的类,还是我误解了这一点?使用您的示例 html,这将是 iframe iframe_xpath = '//*[@class="parent"]/iframe[contains(@style, "display: block")]' 的 xpath
    猜你喜欢
    • 2016-11-19
    • 2021-12-29
    • 2021-12-30
    • 2012-04-24
    • 2013-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多