Как реализовать цепочку методов Selenium для нескольких WebDriverWait в Python - PullRequest
0 голосов
/ 25 ноября 2018

Я хочу реализовать метод цепочки 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 пытался использовать Python 's lambda функция через цепочка методов с использованием 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

После следующих обсуждений:

До сих пор не знаю, что мне не хватает.

Может кто-нибудь подсказать мне, где я ошибаюсь?

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

Редактировать:

Я не знаю, соответствует ли это вашему требованию, но нам нужно создать пользовательскую конструкцию.

@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")

old:


Я не эксперт, но я думаю, что вы, возможно, неправильно понимаете, как работает конвейер, по умолчанию он работает для обработки предыдущего значения, например

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']"))

или селен 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()
0 голосов
/ 28 ноября 2018

Как я понимаю, на самом деле ваше требование - иметь WebDriverWait с двумя ожидаемыми условиями, а не использовать цепочку методов и библиотеку pipe любой ценой.

Вы можете сделать это, нажавнемного синтаксис лямбда-выражения.Самое чистое решение - сделать его перечислимым по вызовам функций и получить возвращаемое значение того, что вам нужно:

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])

С индексом вы получите второй член кортежа, например, tableelement.

Выражение можно переписать в логическое значение:

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;Я бы придерживался 1-го, перечисляемого.

При таком подходе вы получаете преимущества WebDriverWait - если какой-либо из вызовов в кортеже вызовет одно из обработанных исключений, произойдет повторный вызов,и т. д.
Однако при таком подходе есть один недостаток производительности - вызов первого метода будет выполняться в каждом цикле, даже если он уже прошел успешно, а теперь ожидается второе условие.


И, вот совершенно другая альтернатива, самое чистое решение - я вижу, что вы не стесняетесь использовать xpath, поэтому чисто xpath.
Поскольку ваша цель - получить элемент table, если и только если присутствует input, это будет сделано именно так:

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

Это выберет table с ролью.Другое условие для этого - подняться по своим предкам - и ось ancestor поднимется до верхнего узла в DOM - и оттуда найти элемент input с этим атрибутом id.

Таким образом, если input все еще отсутствует, xpath не будет ничего совпадать.В тот момент, когда он доступен, и есть также table с этим значением роли - оно будет возвращено.
Естественно, этот селектор может использоваться непосредственно в WebDriverWait с одним условием:

element = WebDriverWait(driver, 5).until(lambda x: x.find_element_by_xpath("//table[@role='presentation' and ancestor::*//input[@id='email']]"))
0 голосов
/ 25 ноября 2018

Как показывает ваша ошибка:

TypeError: объект 'function' не повторяется

Это потому что:

где () метод Только возвращает соответствующие элементы данной итерируемой, например [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")

Надеюсь, это поможет вам ...

Edit2 :

Я думаю, что вы ищете, чтобы построить вашу единственную трубу, как показано здесь .

...