NoSuchElementException:消息:无法找到元素:[id =" j_username"]尝试WebdriverWait

时间:2017-03-09 19:10:16

标签: python selenium selenium-webdriver

我几乎无法让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

我得到了#34;等待失败"立刻留言

如果我输入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秒,我的错误立即失败。

2 个答案:

答案 0 :(得分:1)

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

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

在调用NoSuchElementException之前失败,wait.until()

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

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

答案 1 :(得分:1)

感谢您的回答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秒)