【问题标题】:Selenium finding elements which load later via javascriptSelenium 查找稍后通过 javascript 加载的元素
【发布时间】:2018-07-24 12:58:29
【问题描述】:

所以我正在尝试使用 selenium 自动完成某些表单,但我遇到了一个问题。我使用的其中一种表单不是由 HTML 立即加载的,而是在页面正常加载后使用 JavaScript 加载的。无论出于何种原因,在 javascript 中加载后,selenium 都无法看到页面的更新源。例如,如果我运行以下代码。

browser = webdriver.Firefox()
browser.get('https://examplepage.com')

WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.ID, “13jres”))).send_keys(“email@email.com”)

什么都没有发生,它会超时。在做了一些测试后,我注意到如果我在 python 中打印源代码,使用以下代码

browser = webdriver.Firefox()
browser.get('https://examplepage.com')
time.sleep(20)
print browser.page_source

然后源代码与我可以在 selenium firefox 实例中手动查看的源代码不同。因此,根据 selenium 源输出,我试图输入的以下行不存在,即使在 Firefox 中检查元素或在 Firefox 实例中查看已加载内容的源时它明显存在使用硒。

<input label=“Email” type="text" name="13jres" id="13jres" class="text-field”>(shortened to make it more readable)

阅读一些文档时,我在引用 page_source 命令时发现了这个花絮,我猜这解释了源代码的差异,但我仍然不清楚如何缓解在页面上查找这些元素的问题。我在 selenium 中尝试过其他浏览器(safari、chrome 等),但除此之外,我不确定我需要做什么。

“如果页面在加载后被修改(例如,通过 Javascript),则不能保证返回的文本就是修改后页面的文本。请查阅正在使用的特定驱动程序的文档,以确定返回的文本是反映页面的当前状态还是 Web 服务器上次发送的文本。”

【问题讨论】:

  • 你睡了 20 秒后,find_element_by_id("13jres") 返回什么?在 Firefox 控制台中,document.getElementById("13jres") 返回什么?
  • 页面加载时是否调用了javascript?
  • 试试这个EC.visibility_of_element_located((By.ID, “13jres”))

标签: javascript python html selenium


【解决方案1】:

正如您提到的Nothing happens and it times out.,这基本上意味着它可以是以下任何一种情况:

  • &lt;input&gt; 标签:根据您提供的 短 HTML

    <input label=“Email” type="text" name="13jres" id="13jres" class="text-field”>(shortened to make it more readable)
    

    由于缩短了标记,我们无法了解&lt;input&gt; 标记是否有任何与之关联的onClick() 事件。

    接下来,您正在尝试:

    WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.ID, “13jres”))).send_keys(“email@email.com”)
    

    我们是否调用右侧 webelement 上的 send_keys() 尚无定论。

  • Locator Strategy:根据您的代码试用,您尝试使用基于id定位器策略。但是 id 属性设置为值 13jres 在我看来是动态的。因此,您可以更细化并适应更有效的Locator Strategy,如下所示:

    WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, “input.text-field[id$='jres']”))).send_keys(“email@email.com”)
    
  • 您可以在Official locator strategies for the webdriver

  • 找到关于定位器策略的详细讨论

【讨论】:

  • 很好的答案,你真的很详细等待+1
【解决方案2】:

尝试等待页面完全加载,然后执行操作。我不在 python 中使用,但在 javascriptexecutor 中有一个选项

bool wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60)).Until(d => ((javascriptexecutor)d).executescript("return document.readyState").Equals("complete")); 

if(wait == true)
{
    //Your code
}

上面的语法可能会因python而改变

以上代码将等待页面加载 60 秒,如果页面准备好(60 秒内)返回 true,如果页面未准备好(60 秒后)返回 false。

【讨论】:

    【解决方案3】:

    基于 page_source 的源代码使用 selenium 进行自动化可能是不好的做法,因为有两种主要情况,而且它们经常发生,实时页面背后的代码与初始网页源页面不同:

    1.

    page_source 显示源页面,但源页面虽然实际上是DOM 的原始种子页面,但DOM 可以更改,并且有时会被JS 代码动态更改。 在这种情况下,最佳做法是:

    browser.get("url")
    sleep(experimental) # usually get will finish only after the page is loaded but sometimes there is some JS woo running after on load time
      
    try:
        element= WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'your_id_of_interest')))
        print "element is ready do the thing!"
    except TimeoutException:
        print "Somethings wrong!"   
    

    2.

    page_source 不显示 shadow DOMS 如果您的元素碰巧在 shadow DOMS 中看到,它将在 JavaScript 中的 page_sourcebrowserdocument 对象中不可见,您需要先展开shadow-DOM

    def expand_shadow_element(element):
      shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
      return shadow_root
    
    outer = expand_shadow_element(driver.find_element_by_css_selector("#test_button"))
    inner = outer.find_element_by_id("inner_button")
    inner.click()
    

    当您在影子根中具有影子根时,问题就来了,要查看更多详细信息,请参阅此答案:Accessing Shadow DOM tree with Selenium

    如果您想了解如何获取动态内容的源代码,您还可以查看我给出的这个答案:https://stackoverflow.com/a/48782708/1577343

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-28
      • 2016-11-25
      • 1970-01-01
      • 2016-03-12
      • 2017-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多