具有以下内容时如何find_element_by_link_text:NoSuchElement异常?

时间:2013-08-02 18:22:51

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

这个问题一再被问到 - 尽管我尝试了所有的黑客攻击,但我仍然无法弄清楚是什么问题。

我尝试将implicitly_wait增加到30(甚至将其增加到100) - 但它没有用。

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

###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)

问题&amp;评论:

  1. 如果您正在回答此问题 - 请提及为什么此特定“find_element_by_link_text”不起作用。

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

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

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

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

  6. 我尝试与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.
    

    相关问题:

4 个答案:

答案 0 :(得分:2)

在我之前发生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')]"]

希望它有所帮助。

答案 1 :(得分:0)

从查看您提供链接的网页来源,您似乎使用了错误的选择器。

你应该使用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)

答案 2 :(得分:0)

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 :(得分:0)

OP发布的解决方案:

Hack 1:我没有将元素标识为文本链接,而是确定了更大的框架&#34;这个元素存在的地方。 itemlist_1 = driver.find_element_by_css_selector(&#34; li.item.first&#34;)。text 这将给出整个项目以及名称,价格和细节(以及不需要的添加到购物车和比较&#34;

有关详情,请参阅附图。 enter image description here

黑客2:我发现&#34;立即购买&#34;这是xPath的一个图像元素(driver.find_element_by_xpath(&#34; // div [@id =&#39; subseries&#39;] / div [2] / div / p [3] / a&#34;) 。点击() ,在上面的代码中,如果我添加以下行,可以更快地点击/识别,然后通过xpath找到它。 我认为这会缩小Webdriver寻找元素的地方。这就是我添加的内容&#34; driver.find_element_by_css_selector(&#34;#子系列&#34)文本&#34;

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