如何在 Python 中实现 Selenium 多个 WebDriverWait 的方法链接
How to implement method chaining of Selenium multiple WebDriverWait in Python
我正在寻求实现 Selenium WebDriverWaits 的方法链接。
首先,实现单个 WebDriverWait 的代码块非常完美:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument('disable-infobars')
driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe')
driver.get('https://www.facebook.com')
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']"))
element.send_keys("method_chaining")
根据我目前的要求,我必须实现两个 WebDriverWait 实例的链接,因为我的想法是获取从第一个 WebDriverWait 返回的元素作为输入到(链接的)第二个 WebDriverWait.
为了实现这一点,我关注了讨论method chaining in python tried to use the use Python's lambda
function through method chaining using Pipe - Python library to use infix notation in Python。
这是我的代码试用:
from pipe import *
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument('disable-infobars')
driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\WebDrivers\chromedriver.exe')
driver.get('https://www.facebook.com')
element = WebDriverWait(driver,15).until((lambda driver: driver.find_element_by_xpath("//input[@id='email']"))
| where(lambda driver: driver.find_element_by_css_selector("table[role='presentation']")))
element.send_keys("method_chaining")
我看到一个错误:
DevTools listening on ws://127.0.0.1:52456/devtools/browser/e09c1d5e-35e3-4c00-80eb-cb642fa273ad
Traceback (most recent call last):
File "C:\Users\Soma Bhattacharjee\Desktop\Debanjan\PyPrograms\python_pipe_example.py", line 24, in <module>
| where(lambda driver: driver.find_elements(By.CSS_SELECTOR,"table[role='presentation']")))
File "C:\Python\lib\site-packages\pipe.py", line 58, in __ror__
return self.function(other)
File "C:\Python\lib\site-packages\pipe.py", line 61, in <lambda>
return Pipe(lambda x: self.function(x, *args, **kwargs))
File "C:\Python\lib\site-packages\pipe.py", line 271, in where
return (x for x in iterable if (predicate(x)))
TypeError: 'function' object is not iterable
关注了以下讨论:
- python3 TypeError: 'function' object is not iterable
仍然不知道我错过了什么。
谁能指导我哪里出错了?
如您的错误所示:
TypeError: 'function' object is not iterable
这是因为:
where() method
Only yields the matching items of the given iterable
for example [1, 2, 3] | where(lambda x: x % 2 == 0) | concat => '2'
希望这能回答 有人可以指导我哪里出错了吗?
编辑:
如果你想使用pipe.where
:
所以你可以用你拥有的相同代码来做一些改变:
您的 element
变量应该是这样的:
element = WebDriverWait(driver,15).until((lambda driver: driver.find_elements(By.XPATH,"//input[@id='email']"))) \
| where(WebDriverWait(driver,15).until(lambda driver: driver.find_elements(By.CSS_SELECTOR,"table[role='presentation']")))
但是您不能对 element
做任何事情,因为它是一个 生成器对象 .
为 "to implement WebDriverWait for two expected conditions" 做的最好的事情就是有两行 WebDriverWait
!
像这样:
presentation_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR,"table[role='presentation']")))
email_element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@id='email']")))
email_element.send_keys("method_chaining")
希望这对你有帮助...
编辑2:
我认为您正在寻找的是如图所示构建一个管道 here。
编辑:
我不知道它是否满足您的要求,但我们需要创建自定义构造。
@Pipe
def where(i, p):
if isinstance(i, list):
return [x for x in i if p(x)]
else:
return i if p(i.find_element_by_xpath('//ancestor::*')) else None
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']") \
| where(lambda y: y.find_element_by_css_selector("table[role='presentation']")))
element.send_keys("method_chaining")
旧:
我不是专家,但我认为您可能误解了管道的工作原理,默认情况下它会处理先前的值,例如
original | filter = result
[a,b,c] | select(b) = b
你想要的可能是 and
运算符
WebDriverWait(driver,15).until(
lambda driver: driver.find_element(By.CSS_SELECTOR,"table[role='presentation']")
and driver.find_element(By.XPATH,"//input[@id='email']"))
或 selenium ActionChains 但没有等待方法,我们需要扩展它
from selenium.webdriver import ActionChains
class Actions(ActionChains):
def wait(self, second, condition):
element = WebDriverWait(self._driver, second).until(condition)
self.move_to_element(element)
return self
Actions(driver) \
.wait(15, EC.presence_of_element_located((By.CSS_SELECTOR, "table[role='presentation']"))) \
.wait(15, EC.element_to_be_clickable((By.XPATH, "//input[@id='email']"))) \
.click() \
.send_keys('method_chaining') \
.perform()
据我了解,您的要求实际上是 WebDriverWait
具有两个预期条件,而不是不惜一切代价使用方法链和 pipe
库。
您可以通过稍微推动 lambda 表达式语法来做到这一点。最干净的解决方案是使它成为可枚举的函数调用,并获取您需要的返回值:
element = WebDriverWait(driver, 5).until(lambda x: (x.find_element_by_xpath("//input[@id='email']"), x.find_element_by_css_selector("table[role='presentation']"))[1])
使用索引,您将收到第二个元组成员,例如table
元素。
表达式可以重写为布尔值:
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']") and x.find_element_by_css_selector("table[role='presentation']"))
您将再次获得第二个元素,即布尔值的最后一部分。但是,当(ab)与太复杂的布尔值一起使用时,此实现有一些缺陷,所以 YMMV;我会坚持第一个,可枚举的。
通过这种方法,您可以获得 WebDriverWait
的好处 - 如果元组中的任何调用引发了一个已处理的异常,将会重试,等等。
不过,这种方法会带来一个性能负面影响 - 对第一个方法的调用将在每个周期执行,即使它已经成功通过并且现在正在等待第二个条件。
而且,这是一个完全不同的替代方案,最干净的解决方案 - 我看到您并不害羞使用 xpath,所以一个纯 xpath 的。
由于您的目标是获取 table
元素,当且仅当 input
存在时,这将做到这一点:
//table[@role='presentation' and ancestor::*//input[@id='email']]
这将select与table
的作用。它的另一个条件是向上移动它的祖先 - ancestor
轴将向上移动到 DOM 中的顶部节点 - 然后从那里向下找到一个 input
元素标识属性。
因此,如果 input
仍然不存在,则 xpath 将不匹配任何内容。一旦它可用,并且还有一个具有该角色值的 table
- 它将被返回。
自然,这个selector可以直接用在WebDriverWait
中,条件单一:
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//table[@role='presentation' and ancestor::*//input[@id='email']]"))
我正在寻求实现 Selenium WebDriverWaits 的方法链接。
首先,实现单个 WebDriverWait 的代码块非常完美:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument('disable-infobars')
driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe')
driver.get('https://www.facebook.com')
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']"))
element.send_keys("method_chaining")
根据我目前的要求,我必须实现两个 WebDriverWait 实例的链接,因为我的想法是获取从第一个 WebDriverWait 返回的元素作为输入到(链接的)第二个 WebDriverWait.
为了实现这一点,我关注了讨论method chaining in python tried to use the use Python's lambda
function through method chaining using Pipe - Python library to use infix notation in Python。
这是我的代码试用:
from pipe import *
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument('disable-infobars')
driver = webdriver.Chrome(chrome_options=options, executable_path=r'C:\WebDrivers\chromedriver.exe')
driver.get('https://www.facebook.com')
element = WebDriverWait(driver,15).until((lambda driver: driver.find_element_by_xpath("//input[@id='email']"))
| where(lambda driver: driver.find_element_by_css_selector("table[role='presentation']")))
element.send_keys("method_chaining")
我看到一个错误:
DevTools listening on ws://127.0.0.1:52456/devtools/browser/e09c1d5e-35e3-4c00-80eb-cb642fa273ad
Traceback (most recent call last):
File "C:\Users\Soma Bhattacharjee\Desktop\Debanjan\PyPrograms\python_pipe_example.py", line 24, in <module>
| where(lambda driver: driver.find_elements(By.CSS_SELECTOR,"table[role='presentation']")))
File "C:\Python\lib\site-packages\pipe.py", line 58, in __ror__
return self.function(other)
File "C:\Python\lib\site-packages\pipe.py", line 61, in <lambda>
return Pipe(lambda x: self.function(x, *args, **kwargs))
File "C:\Python\lib\site-packages\pipe.py", line 271, in where
return (x for x in iterable if (predicate(x)))
TypeError: 'function' object is not iterable
关注了以下讨论:
- python3 TypeError: 'function' object is not iterable
仍然不知道我错过了什么。
谁能指导我哪里出错了?
如您的错误所示:
TypeError: 'function' object is not iterable
这是因为:
where() method Only yields the matching items of the given iterable for example
[1, 2, 3] | where(lambda x: x % 2 == 0) | concat => '2'
希望这能回答 有人可以指导我哪里出错了吗?
编辑:
如果你想使用pipe.where
:
所以你可以用你拥有的相同代码来做一些改变:
您的 element
变量应该是这样的:
element = WebDriverWait(driver,15).until((lambda driver: driver.find_elements(By.XPATH,"//input[@id='email']"))) \
| where(WebDriverWait(driver,15).until(lambda driver: driver.find_elements(By.CSS_SELECTOR,"table[role='presentation']")))
但是您不能对 element
做任何事情,因为它是一个 生成器对象 .
为 "to implement WebDriverWait for two expected conditions" 做的最好的事情就是有两行 WebDriverWait
!
像这样:
presentation_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR,"table[role='presentation']")))
email_element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@id='email']")))
email_element.send_keys("method_chaining")
希望这对你有帮助...
编辑2:
我认为您正在寻找的是如图所示构建一个管道 here。
编辑:
我不知道它是否满足您的要求,但我们需要创建自定义构造。
@Pipe
def where(i, p):
if isinstance(i, list):
return [x for x in i if p(x)]
else:
return i if p(i.find_element_by_xpath('//ancestor::*')) else None
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']") \
| where(lambda y: y.find_element_by_css_selector("table[role='presentation']")))
element.send_keys("method_chaining")
旧:
我不是专家,但我认为您可能误解了管道的工作原理,默认情况下它会处理先前的值,例如
original | filter = result
[a,b,c] | select(b) = b
你想要的可能是 and
运算符
WebDriverWait(driver,15).until(
lambda driver: driver.find_element(By.CSS_SELECTOR,"table[role='presentation']")
and driver.find_element(By.XPATH,"//input[@id='email']"))
或 selenium ActionChains 但没有等待方法,我们需要扩展它
from selenium.webdriver import ActionChains
class Actions(ActionChains):
def wait(self, second, condition):
element = WebDriverWait(self._driver, second).until(condition)
self.move_to_element(element)
return self
Actions(driver) \
.wait(15, EC.presence_of_element_located((By.CSS_SELECTOR, "table[role='presentation']"))) \
.wait(15, EC.element_to_be_clickable((By.XPATH, "//input[@id='email']"))) \
.click() \
.send_keys('method_chaining') \
.perform()
据我了解,您的要求实际上是 WebDriverWait
具有两个预期条件,而不是不惜一切代价使用方法链和 pipe
库。
您可以通过稍微推动 lambda 表达式语法来做到这一点。最干净的解决方案是使它成为可枚举的函数调用,并获取您需要的返回值:
element = WebDriverWait(driver, 5).until(lambda x: (x.find_element_by_xpath("//input[@id='email']"), x.find_element_by_css_selector("table[role='presentation']"))[1])
使用索引,您将收到第二个元组成员,例如table
元素。
表达式可以重写为布尔值:
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//input[@id='email']") and x.find_element_by_css_selector("table[role='presentation']"))
您将再次获得第二个元素,即布尔值的最后一部分。但是,当(ab)与太复杂的布尔值一起使用时,此实现有一些缺陷,所以 YMMV;我会坚持第一个,可枚举的。
通过这种方法,您可以获得 WebDriverWait
的好处 - 如果元组中的任何调用引发了一个已处理的异常,将会重试,等等。
不过,这种方法会带来一个性能负面影响 - 对第一个方法的调用将在每个周期执行,即使它已经成功通过并且现在正在等待第二个条件。
而且,这是一个完全不同的替代方案,最干净的解决方案 - 我看到您并不害羞使用 xpath,所以一个纯 xpath 的。
由于您的目标是获取 table
元素,当且仅当 input
存在时,这将做到这一点:
//table[@role='presentation' and ancestor::*//input[@id='email']]
这将select与table
的作用。它的另一个条件是向上移动它的祖先 - ancestor
轴将向上移动到 DOM 中的顶部节点 - 然后从那里向下找到一个 input
元素标识属性。
因此,如果 input
仍然不存在,则 xpath 将不匹配任何内容。一旦它可用,并且还有一个具有该角色值的 table
- 它将被返回。
自然,这个selector可以直接用在WebDriverWait
中,条件单一:
element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//table[@role='presentation' and ancestor::*//input[@id='email']]"))