Как найти_элемент_по_ссылке_текста, имея: Исключение NoSuchElement?

Этот вопрос задавали снова и снова - и, несмотря на то, что я попробовал все хаки, я до сих пор не могу понять, что не так.

Я попытался увеличить значение параметра implicitly_wait до 30 (и даже до 100), но это не сработало.

Случай использования –: я пытаюсь создать список, в который будут включены все элементы на странице здесь, в качестве базового случая, и я намерен связать это с мини-модулем, который у меня уже есть, с помощью 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/[email protected]/components/driver_component.js:8444)
    at fxdriver.Timer.prototype.setTimeout/<.notify (file:///tmp/tmphli5Jg/extensions/[email protected]/components/driver_component.js:386) 

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

FAILED (errors=1)

Вопросы и комментарии:

  1. Если вы отвечаете на этот вопрос, укажите, почему этот конкретный «find_element_by_link_text» не работает.

  2. (Очень простой) В графическом интерфейсе моей Selenium IDE -> Показать все доступные команды - почему я не вижу 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.

Связанные вопросы:


person ekta    schedule 02.08.2013    source источник
comment
Я понятия не имею, почему ссылка не работает. Работает ли селектор css .product-name a[title='Thinkpad Edge E530 (Black)']?   -  person Nathan Merrill    schedule 02.08.2013


Ответы (4)


Со мной и раньше случалось, что метод find_element_by_link_text иногда работает, а иногда нет; даже в единичном случае. Я думаю, что это ненадежный способ доступа к элементам; лучше всего использовать find_element_by_id.

Но в вашем случае, когда я захожу на страницу, идентификатор не может вам помочь. Тем не менее вы можете попробовать find_elements_by_xpath тремя способами:

1- Доступ к заголовку: find_element_by_xpath["//a[contains(@title = 'T430')]"]

2- Доступ к тексту: find_element_by_xpath["//a[contains(text(), 'T430')]"]

3- Доступ к ссылке: find_element_by_xpath["//a[contains(@href = 'http://www.thedostore.com/laptops/thinkpad-laptops/thinkpad-t430-u-black-627326q.html')]"].

Надеюсь, поможет.

person Zeinab Abbasimazar    schedule 26.09.2013
comment
Это действительно помогло мне, я бы выбрал это как свой принятый ответ, учитывая, что в одном и том же возрасте один find_element_by_link_text отлично работает для одной ссылки, но не для другой ссылки. Я не уверен, но похоже, что это может быть связано с пробелами в значении. - person schwarz; 11.06.2014

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. #P7#
    (Pdb) driver.execute_script("return document.readyState")
    'complete'
    
    #P8# <цитата> #P9#
  3. Если вы считаете, что это происходит из-за того, что страница не была загружена должным образом, «рекомендуемым» (хотя и некрасивым) решением является явное ожидание:

    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)
    

    И последний пример включает сравнение идентификаторов страниц, как показано ниже (что может быть пуленепробиваемым):

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

    Приведенные выше примеры кода взяты из блог Гарри.

  4. Вот версия, предложенная Томми Бидл (с помощью устаревший подход):

    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 или другом окне. Если это так, вам нужно сначала переключиться на него. Чтобы проверить список доступных окон, запустите: driver.window_handles.

person kenorb    schedule 17.05.2015

При просмотре источника страницы, на которую вы предоставили ссылку, кажется, что вы используете неверный селектор.

Вместо этого вы должны использовать 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)
person Nicholas Murray    schedule 26.09.2013

Решение, опубликованное OP:

Хак 1: вместо того, чтобы идентифицировать элемент как текстовую ссылку, я определил «большую рамку», в которой присутствовал этот элемент. itemlist_1 = driver.find_element_by_css_selector("li.item.first").text Это даст весь товар вместе с именем, ценой и деталями (и нежелательным добавлением в корзину и сравнением)

См. прикрепленное изображение для получения дополнительной информации. введите здесь описание изображения

Хак 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 секунд. Надеюсь, это поможет.

person Community    schedule 18.05.2015