我几乎无法让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秒,我的错误立即失败。
答案 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秒)