【问题标题】:NoSuchElementException: Message: Unable to locate element: [id="j_username"] attempting WebdriverWaitNoSuchElementException:消息:无法找到元素:[id="j_username"] 尝试 WebdriverWait
【发布时间】:2017-07-30 22:10:35
【问题描述】:

我几乎永远无法让 webdriver 等待工作,我不明白为什么。我遵循为其他人工作的示例,最近是How to wait until the page is loaded with Selenium for Python?

我在这里

import os
import time

from gitflow.config import PIVOTAL_BASE_URL, WORK_EMAIL, WORK_USERNAME
from gitflow.webdriver_base import FirefoxDriver

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException


class PivotalTrackerDriver(object):

    def __init__(self):
        self.username = WORK_EMAIL
        self.password = os.environ.get('WORK_PW')
        self.main_url  = PIVOTAL_BASE_URL
        self.login_url = self.main_url.rstrip('/') + "/signin"
        self.driver = FirefoxDriver()

    def login(self):
        self.driver.get(self.login_url)
        time.sleep(2)
        self.driver.find_box_and_fill(search_text="credentials_username", value=self.username)
        self.driver.click_button("NEXT")
        try:
            WebDriverWait(self.driver, 5).until(EC.presence_of_element_located(self.driver.find_element_by_id("j_username")))
            print("\n\ngot it")
        except:
            print("\n\nwaiting failed")
        self.driver.find_box_and_fill(search_text="j_username", value="cody")
        self.driver.find_box_and_fill(search_text="j_password", value=self.password)
        self.driver.click_button("Sign In")
        time.sleep(2)

    def ensure_logged_in(self):
        if not self.is_logged_in:
            self.login()

    @property
    def is_logged_in(self):
        contents = self.driver.soup.encode_contents()
        return "Cody" in contents

我立即收到“等待失败”消息

如果我进入shell ipdb,加载的页面肯定有j_username:

ipdb> element = self.driver.locate_element("j_username")
ipdb> element
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="2aab73e6-e6c3-bf4d-b500-292d5caf5cc5", element="c9a56c8a-1271-6b48-af95-1f75ecdaaabc")>

我的 Selenium 包装器看起来像:

import os, time, subprocess, random

try:
    from bs4 import BeautifulSoup
except ImportError:
    import pip
    pip.main(['install', 'bs4'])
    from bs4 import BeautifulSoup

try:
    from selenium import webdriver
except ImportError:
    import pip
    pip.main(['install', 'selenium'])
    from selenium import webdriver

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import TimeoutException


"""
Important links:
https://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.action_chains.html
"""


class WebdriverChauffuerMixin(object):

    @property
    def soup(self, **kwargs):
        return BeautifulSoup(self.page_source, 'html.parser')

    def locate_element(self, search_text=None, xpath=None):
        if not xpath:
            xpaths = [
                "//input[@value='{text}']",
                "//button[normalize-space(text())='{text}']",
                "//a[child::span[normalize-space(text())='{text}']]",
                "//a[normalize-space(text())='{text}']",
                "//button[contains(.,'{text}')]",
                "//input[contains(.,'{text}')]",
                "//span[contains(.,'{text}')]",
            ]
        else:
            return self.find_element_by_xpath(xpath)
        try:
            return self.find_element_by_id(search_text)
        except:
            try:
                return self.find_element_by_name(search_text)
            except:
                try:
                    return self.find_element_by_class_name(search_text)
                except:
                    for path in xpaths:
                        try:
                            return self.find_element_by_xpath(path.format(text=search_text))
                        except:
                            pass
        return None

    @property
    def active_element(self):
        return self.switch_to_active_element()

    def tab_through(self):
        self.active_element.send_keys(Keys.TAB)

    def click_active_by_hitting_enter(self):
        self.active_element.send_keys(Keys.ENTER)

    def activate_hidden_element(self, search_text=None):
        action = webdriver.ActionChains(self)
        element = self.locate_element(search_text)
        action.move_to_element(element).click().perform()
        return element

    def access_link(self, search_text=None):
        element = self.locate_element(search_text)
        link  = element.get_attribute('href')
        self.get(link)

    def submit_form(self, search_text=None):
        form = self.locate_element(search_text)
        form.submit()

    def click_button(self, search_text=None, xpath=None):
        element = self.locate_element(search_text, xpath)
        try:
            element.click()
        except:
            self.mouse.move_to_element(element).click().perform()

    def click_anything(self, search_text=None, xpath=None):
        self.locate_element(search_text, xpath).click()

    @property
    def mouse(self):
        return webdriver.ActionChains(self)

    def find_box_and_fill(self, value=None, search_text=None, xpath=None):
        """locate_element can take search text or xpath"""
        box = self.locate_element(search_text=search_text, xpath=xpath)
        try:
            box.clear()
        except:
            pass
        box.send_keys(value)

    def find_box_and_fill_as_person(self, search_text=None, value=None):
        box = self.locate_element(search_text)
        for character in value:
            box.send_keys(character)
            self.sleep_random(5, 12)

    def sleep_random(self, start, end):
        time.sleep(self.get_random_time(start, end))

    def get_random_time(self, start, end):
        diff = end - start
        random_num = int((random.SystemRandom(random.seed()).random()) * diff)
        actual_num = (random_num + start)
        return self.convert_num_to_secs(actual_num)

    def convert_num_to_secs(self, num):
        """takes a number like 89 and converts it to 0.89 seconds"""
        return (float(num)/float(100))

    def pick_select_box(self, search_text=None, value=None):
        element = self.locate_element(search_text)
        select_bot = Select(element)
        select_bot.select_by_value(value)

    def pick_radio_button(self, value):
        try:
            value.click()
        except AttributeError:
            element = self.locate_element(value)
            element.click()

    def cycle_through_tabs(self):
        self.find_element_by_tag_name('body').send_keys(Keys.CONTROL + Keys.TAB)

    def open_new_tab(self):
        self.find_element_by_tag_name('body').send_keys(Keys.CONTROL + 't')

    def complete_field(self, selection, search_text):
        # TODO: the minor 'AI' function that tries to predict the value to input should be
        #       separate from the logic to make the selection
        #       (based on tag type like input, select, checkbox, etc)

        # selection can be True or False for checkbox, text for input box, etc
        try:
            selection = getattr(selection, search_text)
        except:
            pass
        if not isinstance(selection, bool):
             selection = str(selection)
        element = self.locate_element(search_text)
        tag_type = element.tag_name
        if tag_type == 'input':
            input_type = element.get_attribute('type')
            if input_type == 'checkbox':
                if selection:
                    self.click_anything(search_text)
            elif input_type in ['text', 'textarea', 'number']:
                self.find_box_and_fill(selection, search_text=search_text)
            else:
                raise Exception('Unknown input HTML element type')
        elif tag_type == 'select':
            if isinstance(selection, bool):
                selection = "1" if selection else "0"
            print('\n\nselection: ', selection)
            self.pick_select_box(value=selection, search_text=search_text)
        elif tag_type == 'textarea':
            self.find_box_and_fill(selection, search_text=search_text)
        else:
            if not selection:
                return None
            else:
                raise Exception('Unknown HTML element type')
        self.basic_sleep('short')

    def basic_sleep(self, length=None):
        if length == 'short':
            self.sleep_random(120, 250)
        elif length == 'long':
            self.sleep_random(700, 1000)
        else:
            self.sleep_random(300, 700)

    def wait(self, search_item=None, search_type="id"):
        # search_type like id, name, xpath, etc
        BY_MAP = {
        'id': By.ID,
        'name': By.NAME,
        'class': By.CLASS_NAME,
        'xpath': By.XPATH,
        }
        try:
            method = "find_element_by_" + search_type
            element = getattr(self, method)(search_item)
            # WebDriverWait(self, 15).until(EC.presence_of_element_located((BY_MAP[search_type], search_item)))
            WebDriverWait(self, 15).until(EC.presence_of_element_located(getattr(self, method)(search_item)))
            print("Page is ready!")
        except TimeoutException:
            print("Loading took too much time!")


class ChromeDriver(WebdriverChauffuerMixin, webdriver.Chrome):
    pass

class FirefoxDriver(WebdriverChauffuerMixin, webdriver.Firefox):
    pass

我认为 webdriverwait 将持续检查元素长达 x 秒,我的立即失败。

【问题讨论】:

    标签: python selenium selenium-webdriver


    【解决方案1】:

    问题是,您实际上是在 调用 find_element_by_id() 方法:

    WebDriverWait(self, 15).until(EC.presence_of_element_located(getattr(self, method)(search_item)))
                                                                                  HERE^
    

    即使在调用wait.until() 之前,它也会以NoSuchElementException 失败。


    相反,您需要将定位器本身传递给预期条件:

    wait = WebDriverWait(self, 15)
    wait.until(EC.presence_of_element_located((BY_MAP[search_type], search_item)))
    

    【讨论】:

      【解决方案2】:

      感谢这个答案python selenium wait for page to load,我能够动态解决大多数常见元素的通用WebdriverWait,例如:

       def wait(self, search_item=None):
              HTML_ELEMENT_BY_STYLES = [By.ID, By.NAME, By.CLASS_NAME, By.XPATH]
              ready = False
              # since each check waits 0.2 seconds, the total wait time is WAIT_TIME * 5
              WAIT_TIME = 15 # seconds
              for i in range(int(WAIT_TIME * 5)):
                  if ready == True:
                      break
                  for by_element in HTML_ELEMENT_BY_STYLES:
                      try:
                          WebDriverWait(self, 0.2).until(EC.presence_of_element_located((by_element, search_item)))
                          ready = True
                          break
                      except TimeoutException:
                          print("Loading took too much time!")
      

      这是有效率的(最多浪费大约0.2秒)

      【讨论】:

        猜你喜欢
        • 2019-06-08
        • 1970-01-01
        • 2019-07-25
        • 2017-09-15
        • 1970-01-01
        • 1970-01-01
        • 2019-08-08
        • 1970-01-01
        • 2019-04-25
        相关资源
        最近更新 更多