【问题标题】:How to find_element_by_link_text while having: NoSuchElement Exception?如何在有以下情况时找到_element_by_link_text:NoSuchElement 异常?
【发布时间】:2013-08-04 02:59:26
【问题描述】:

这个问题被问了一遍又一遍——尽管尝试了所有的技巧,但我似乎仍然无法找出问题所在。

我尝试将 implicitly_wait 增加到 30(甚至增加到 100) - 但它不起作用。

用例 -:我正在尝试创建一个列表,该列表将填充页面 here 中的所有项目,作为基本案例 - 我打算将此绑定到我已经使用scrapy拥有的迷你模块,该模块具有所有(具有相似网络元素的页面)已爬网链接 - 所以基本上将构建整个管道,我完成了这个。

###My source code - generated via Selenium IDE, exported to a Python webdriver and manipulated a little later ###

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support.wait import WebDriverWait
import unittest, time, re

class Einstein(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "http://shopap.lenovo.com/in/en/laptops/"
        self.verificationErrors = []
        self.accept_next_alert = True

    def test_einstein(self):
        driver = self.driver
        driver.get(self.base_url)
        print driver.title
        driver.find_element_by_link_text("T430").click()
        print driver.title
#       driver.find_element_by_xpath("id('facetedBrowseWrapper')/div/div/div[1]/div[2]/ul[1]/li[1]/a").click()
        driver.find_element_by_xpath("//div[@id='subseries']/div[2]/div/p[3]/a").click()
        print driver.title
       # driver.find_element_by_xpath("//div[@id='subseries']/div[2]/div/p[3]/a").click()
        try: self.assertEqual("Thinkpad Edge E530 (Black)", driver.find_element_by_link_text("Thinkpad Edge E530 (Black)").text)
        except AssertionError as e: self.verificationErrors.append(str(e))
       # Everything ok till here

        #**THE CODE FAILS HERE**#
        laptop1 = driver.find_element_by_link_text("Thinkpad Edge E530 (Black)").text
        print laptop1
        price1 = driver.find_element_by_css_selector("span.price").text
        print price1
        detail1 = self.is_element_present(By.CSS_SELECTOR, "div.desc.std")
        print detail1

            def is_element_present(self, how, what):
        try: self.driver.find_element(by=how, value=what)
        except NoSuchElementException, e: return False
        return True

    def is_alert_present(self):
        try: self.driver.switch_to_alert()
        except NoAlertPresentException, e: return False
        return True

    def close_alert_and_get_its_text(self):
        try:
            alert = self.driver.switch_to_alert()
            alert_text = alert.text
            if self.accept_next_alert:
                alert.accept()
            else:
                alert.dismiss()
            return alert_text
        finally: self.accept_next_alert = True

    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)

if __name__ == "__main__":
    unittest.main()


Errors & output :
ekta@ekta-VirtualBox:~$ python einstein.py
Laptops & Ultrabooks | Lenovo (IN)
ThinkPad T430 Laptop PC for Business Computing | Lenovo (IN)
Buy Lenovo Thinkpad Laptops | Lenovo Thinkpad Laptops Price India
E
======================================================================
ERROR: test_einstein (__main__.Einstein)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "einstein.py", line 27, in test_einstein
    try: self.assertEqual("Thinkpad Edge E530 (Black)", driver.find_element_by_link_text("Thinkpad Edge E530 (Black)").text)
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py", line 246, in find_element_by_link_text
    return self.find_element(by=By.LINK_TEXT, value=link_text)
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py", line 680, in find_element
    {'using': by, 'value': value})['value']
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py", line 165, in execute
    self.error_handler.check_response(response)
  File "/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/errorhandler.py", line 158, in check_response
    raise exception_class(message, screen, stacktrace)
NoSuchElementException: Message: u'Unable to locate element: {"method":"link text","selector":"Thinkpad Edge E530 (Black)"}' ; Stacktrace: 
    at FirefoxDriver.prototype.findElementInternal_ (file:///tmp/tmphli5Jg/extensions/fxdriver@googlecode.com/components/driver_component.js:8444)
    at fxdriver.Timer.prototype.setTimeout/<.notify (file:///tmp/tmphli5Jg/extensions/fxdriver@googlecode.com/components/driver_component.js:386) 

----------------------------------------------------------------------
Ran 1 test in 79.348s

FAILED (errors=1)

问题和问题:

  1. 如果您正在回答这个问题 - 请说明为什么这个特定的“find_element_by_link_text”不起作用。

  2. (非常基本)在我的 selenium IDE 的 GUI 中 -> 显示所有可用命令 - 为什么我看不到所有 Web 元素的 css (find_element_by_css_selector) - 有没有办法强制输入要作为 CSS 选择器读取的元素?

  3. 如果您建议使用其他定位器 - 请说明这是否是获取元素的一致方式,给定 (1)

  4. 是否断言工作以捕获异常并“继续” - 因为即使在尝试“验证”、“断言”循环之后,我仍然无法获取此“find_element_by_link_text”

  5. 我尝试使用 Xpath 构建这个“元素”,但在 Xpath 视图中(在 firefox 中) - 我什么也没看到,以提示为什么会发生这种情况(当然我删除了命名空间“:x”)

除了implicity_wait(30)之外我尝试过的其他事情:

find_element_by_partial_link(“Thinkpad”) and appending Unicode to this (wasn’t sure if it was reading the brackets ( , driver.find_element_by_link_text(u"Thinkpad Edge E530 (Black)").text, still did not work.

相关问题:

【问题讨论】:

  • 我不知道为什么链接不起作用。 .product-name a[title='Thinkpad Edge E530 (Black)'] 的 CSS 选择器是否有效?

标签: python xpath selenium-webdriver css-selectors webautomation


【解决方案1】:

OP 发布的解决方案:

Hack 1:我没有将元素标识为文本链接,而是标识了该元素所在的“更大的框架”。 itemlist_1 = driver.find_element_by_css_selector("li.item.first").text 这将给出整个项目以及名称、价格和详细信息(以及不需要的添加到购物车和比较”

更多信息请参见附图。

Hack 2:我发现“立即购买”是一个带有 xPath 的图像元素 (driver.find_element_by_xpath("//div[@id='subseries']/div[2] /div/p[3]/a").click() ,在上面的代码中),如果我在通过 xpath 找到它之前添加以下行,可以更快地单击/识别。 我认为这样可以缩小 Webdriver 查找元素的范围。这是我添加的“driver.find_element_by_css_selector("#subseries").text"

这一定使我在该页面上的等待时间减少了至少 20 秒。希望对您有所帮助。

【讨论】:

    【解决方案2】:

    当找不到元素时抛出NoSuchElementException

    如果您遇到此异常,请检查以下内容:

    • 检查您在find_by... 中使用的选择器
    • 在查找操作时元素可能尚未出现在屏幕上。

    如果网页仍在加载,请检查 selenium.webdriver.support.wait.WebDriverWait() 并编写等待包装器以等待元素出现。

    疑难解答和代码示例

    您可以在失败行之前添加断点pdb.set_trace()(不要忘记import pdb),然后运行您的测试,一旦您的调试器停止,然后执行以下测试。

    1. 你可以试试:

      driver.find_element_by_xpath(u'//a[text()="Foo text"]')
      

      相反。这是更可靠的测试,所以如果这可行,请改用它。

    2. 如果以上没有帮助,请检查您的页面是否已通过以下方式正确加载:

      (Pdb) driver.execute_script("return document.readyState")
      'complete'
      

      有时当页面未加载时,您实际上是从旧页面获取元素。但即便如此,readyState 仍然可以指示旧页面的状态(尤其是在使用click() 时)。这是blog中的解释方式:

      由于 Selenium webdriver 变得更加先进,点击更像是“真正的”点击,这有利于我们的测试更加真实,但这也意味着 Selenium 很难跟踪点击的影响在浏览器的内部——它可能会在点击后立即尝试轮询浏览器的页面加载状态,但这对浏览器进行多任务处理的竞争条件是开放的,还没有完全处理点击,它会为您提供旧页面的.readyState

    3. 如果您认为发生这种情况是因为页面未正确加载,“推荐”(但仍然很难看)解决方案是 explicit wait

      from selenium.webdriver.common.by import By
      from selenium.webdriver.support.ui import WebDriverWait 
      from selenium.webdriver.support import expected_conditions
      
      old_value = browser.find_element_by_id('thing-on-old-page').text
      browser.find_element_by_link_text('my link').click()
      WebDriverWait(browser, 3).until(
          expected_conditions.text_to_be_present_in_element(
              (By.ID, 'thing-on-new-page'),
              'expected new text'
          )
      )
      

      天真的尝试是这样的:

      def wait_for(condition_function):
          start_time = time.time()
          while time.time() < start_time + 3:
              if condition_function():
                  return True
              else:
                  time.sleep(0.1)
          raise Exception(
              'Timeout waiting for {}'.format(condition_function.__name__)
          )
      
      
      def click_through_to_new_page(link_text):
          browser.find_element_by_link_text('my link').click()
      
          def page_has_loaded():
              page_state = browser.execute_script(
                  'return document.readyState;'
              ) 
              return page_state == 'complete'
      
          wait_for(page_has_loaded)
      

      另一个更好的是(感谢@ThomasMarks):

      def click_through_to_new_page(link_text):
          link = browser.find_element_by_link_text('my link')
          link.click()
      
          def link_has_gone_stale():
              try:
                  # poll the link with an arbitrary call
                  link.find_elements_by_id('doesnt-matter') 
                  return False
              except StaleElementReferenceException:
                  return True
      
          wait_for(link_has_gone_stale)
      

      最后一个示例包括如下比较页面 id(这可能是防弹的):

      class wait_for_page_load(object):
      
          def __init__(self, browser):
              self.browser = browser
      
          def __enter__(self):
              self.old_page = self.browser.find_element_by_tag_name('html')
      
          def page_has_loaded(self):
              new_page = self.browser.find_element_by_tag_name('html')
              return new_page.id != self.old_page.id
      
          def __exit__(self, *_):
              wait_for(self.page_has_loaded)
      

      现在我们可以这样做了:

      with wait_for_page_load(browser):
          browser.find_element_by_link_text('my link').click()
      

      以上代码示例来自Harry's blog

    4. 这是Tommy Beadle提出的版本(使用staleness的方法):

      import contextlib
      from selenium.webdriver import Remote
      from selenium.webdriver.support.ui import WebDriverWait
      from selenium.webdriver.support.expected_conditions import staleness_of
      
      class MyRemote(Remote):
          @contextlib.contextmanager
          def wait_for_page_load(self, timeout=30):
              old_page = self.find_element_by_tag_name('html')
              yield
              WebDriverWait(self, timeout).until(staleness_of(old_page))
      
    5. 如果您认为这与页面加载无关,请仔细检查您的元素是否不在 iframe 或其他窗口中。如果是这样,你必须switch to it first。要检查可用窗口列表,请运行:driver.window_handles

    【讨论】:

      【解决方案3】:

      从查看您提供链接的页面来源来看,您似乎使用了不正确的选择器。

      您应该改用 find_elements_by_link_text(u'text here')[0] 来选择第一个匹配项,因为似乎存在具有相同链接文本的多个链接。

      所以而不是:

      self.assertEqual("Thinkpad Edge E530 (Black)", driver.find_element_by_link_text("Thinkpad Edge E530 (Black)").text)
      

      你应该使用:

      self.assertEqual("Thinkpad Edge E530 (Black)", driver.find_elements_by_link_text("Thinkpad Edge E530 (Black)")[0].text)
      

      【讨论】:

        【解决方案4】:

        我之前遇到过find_element_by_link_text方法有时有效,有时无效;即使在一个案例中。我认为这不是访问元素的可靠方法;最好的方法是使用find_element_by_id

        但是在您的情况下,当我访问该页面时,没有 id 可以帮助您。您仍然可以通过 3 种方式尝试find_elements_by_xpath

        1- 访问标题:find_element_by_xpath["//a[contains(@title = 'T430')]"]

        2- 访问文本:find_element_by_xpath["//a[contains(text(), 'T430')]"]

        3- 访问href:find_element_by_xpath["//a[contains(@href = 'http://www.thedostore.com/laptops/thinkpad-laptops/thinkpad-t430-u-black-627326q.html')]"]

        希望对你有帮助。

        【讨论】:

        • 它确实对我有帮助,考虑到在同一年龄,一个 find_element_by_link_text 对一个链接有效,但对另一个链接无效,我会选择这个作为我接受的答案。我不确定,但它看起来可能与值中的空格有关。
        猜你喜欢
        • 1970-01-01
        • 2021-12-26
        • 2021-12-27
        • 2021-01-12
        • 1970-01-01
        • 2018-10-28
        • 1970-01-01
        • 2013-11-23
        • 1970-01-01
        相关资源
        最近更新 更多