簡體   English   中英

如何在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的輸入。

為了實現這一點,我按照python中的鏈接討論方法嘗試使用Pythonlambda函數通過方法鏈接使用Pipe-Python庫在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

遵循以下討論:

仍然不知道我錯過了什么。

有人可以指導我在哪里出錯嗎?

編輯:

我不知道它是否符合您的要求,但我們需要創建自定義構造。

@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的好處 - 如果元組中的任何調用引發了一個已處理的異常,則會有一個rertry等。
這種方法有一個性能負面因素 - 第一個方法的調用將在每個周期執行,即使它已經成功通過,現在等待第二個條件。


而且,這是一個完全不同的選擇,最干凈的解決方案 - 我看到你並不害羞使用xpath,所以一個純xpath。
因為您的目標是獲取table元素,當且僅當input存在時,這將執行以下操作:

//table[@role='presentation' and ancestor::*//input[@id='email']]

這將選擇具有角色的table 它的另一個條件是上升它的祖先 - 並且ancestor軸將上升到DOM中的頂層節點 - 並從那里找到具有該id屬性的input元素。

因此,如果input仍然不存在,則xpath將不匹配任何內容。 它可用的那一刻,還有一個具有該角色價值的table - 它將被退回。
當然,這個選擇器可以在WebDriverWait直接使用,只有一個條件:

element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//table[@role='presentation' and ancestor::*//input[@id='email']]"))

正如您的錯誤所示:

TypeError:'function'對象不可迭代

這是因為:

where()方法僅生成給定iterable的匹配項,例如[1, 2, 3] | where(lambda x: x % 2 == 0) | concat => '2' [1, 2, 3] | where(lambda x: x % 2 == 0) | concat => '2'

希望這個答案有人可以指導我哪里出錯了嗎?

編輯:

如果你想使用pipe.where :所以你可以使用相同的代碼進行一些更改:你的element var應該是這樣的:

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做任何事情,因為它是一個生成器對象

“為兩個預期條件實現WebDriverWait”最好的辦法就是擁有兩行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

我想你在找什么是建造一個管如圖所示這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM