Selenium fournit des ʻexpected_conditions` pour examiner les changements d'état des éléments. Mais,
De nombreux aspects sont difficiles à utiliser. Le but de cet article est d'essayer de s'en débarrasser.
Il est destiné à ceux qui ont utilisé du sélénium pour le moment.
Cependant, ce n'est pas pour les utilisateurs avancés.
Pour ceux qui savent ce qu'est XPATH ou find_element ()
.
Python 3.8.3 selenium 3.141.0 geckodriver v0.26.0 Firefox 77.0.1 (64 bits)
Pour le moment, seule la source du résultat est affichée sans explication. (Bien que ce soit Python, ce n'est pas snake_case mais camelCase, il semble donc qu'une pierre puisse être lancée) Je ne l'ai pas complètement testé car il y a des branches conditionnelles que je n'ai pas utilisées.
De plus, dans cet article, nous partirons du principe que le module présenté dans la source suivante est ʻimport`.
python
import logging
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import (UnexpectedAlertPresentException, NoAlertPresentException,
ElementNotVisibleException, TimeoutException, NoSuchElementException)
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class CheckState():
def __init__(self, locator=(), element=None, state="enabled"):
self.locator = locator
self.element = element
self.state = state
def __call__(self, driver):
try:
if self.element is not None and self.locator == ():
element = self.element
elif self.element is not None and self.locator != ():
element = self.element.find_element(*self.locator)
elif self.locator != ():
element = driver.find_element(*self.locator)
else:
return False
if self.state == "enabled":
return element if element.is_enabled() == True else False
elif self.state == "disabled":
return element if element.is_enabled() == False else False
elif self.state == "selected":
return element if element.is_selected() == True else False
elif self.state == "unselected":
return element if element.is_selected() == False else False
elif self.state == "displayed":
return element if element.is_displayed() == True else False
elif self.state == "undisplayed":
return element if element.is_displayed() == False else False
elif self.state == "clickable":
if element.is_enabled() == False:
return False
return element if element.is_displayed() == True else False
else:
return False
except Exception as e:
logger.debug(f"CheckState: {type(e)}, {e}, {self.locator}, {self.element}, {self.state}")
return False
def findElement(driver, locator=(), element=None, state="enabled", must=True, wait=30, interval=0.5, ignore=False):
try:
if element is None and locator == ():
raise ValueError
driverWait = WebDriverWait(driver, wait, interval)
return driverWait.until(CheckState(locator=locator, element=element, state=state))
except TimeoutException:
if must == True and ignore == False:
logger.error(f"findElement: {locator}, {element}, {state}, {must}, {wait}, {interval}, {ignore}")
raise ValueError
return None
except Exception as e:
if ignore == True:
return None
logger.error(f"findElement: {type(e)}, {e}")
raise e
def isDriver(driver):
if isinstance(driver, webdriver.remote.webdriver.WebDriver):
return True
return False
Par exemple
element = driver.find_element(by, value)
Si vous obtenez l'élément par
--ʻElement.is_enabled () : S'il est ʻenabled
--ʻElement.is_displayed () : s'il est affiché à l'écran --ʻElement.is_selected ()
: s'il est sélectionné
--ʻElement.get_attribute (name) : Récupère l'attribut ou la propriété --ʻElement.get_property (name)
: Récupère la propriété
--ʻElement.value_of_css_property (property_name) `: Récupère la valeur de la propriété CSS
Vous pouvez vérifier l'état avec.
Dans cet article, je voudrais considérer le plus simple ʻelement.is_enabled () , ʻelement.is_displayed ()
, ʻelement.is_selected () `.
Dans ʻexpected_conditions`,
element.is_displayed()
element.is_selected()
Les fonctions suivantes sont fournies pour vérifier le changement d'état de.
--ʻEC.element_to_be_selected (element) : Si ʻelement
est ʻis_selected () == True --ʻEC.element_located_to_be_selected (locator)
: Si l'élément indiqué par locator
est ʻis_selected () == True --ʻEC.element_selection_state_to_be (element, is_selected)
: Si ʻelement est ʻis_selected () == is_selected
--ʻEC.element_located_selection_state_to_be (locator, is_selected) : ʻis_selected () == is_selected
--ʻEC.visibility_of (element) : Si ʻelement
est ʻis_displayed () == True --ʻEC.visibility_of_element_located (locator)
: Si l'élément indiqué par locator
est ʻis_displayed () == True --Si ʻEC.invisibility_of_element (element)
: ʻelement est ʻis_displayed () == False
--ʻEC.invisibility_of_element_located (locator) : Si l'élément indiqué par
locator est ʻis_displayed () == False
L'argument ici est
element
:element = driver.find_element(By.XPATH, "//div[@class='cat']")
Passez l'élément ʻelement obtenu par etc. --
locator:
(By.XPATH," // div [@ class = 'cat'] ") etc. est passé. --ʻIs_selected
: Passez True
si vous voulez détecter l'état sélectionné, sinon passez False
.Ce sera. D'autre part
element.is_enabled()
La fonction à vérifier uniquement n'est pas préparée et peut être remplacée
expected_conditions.element_to_be_clickable(locator)
Ce sera.
Cependant, cliquable
ne sert souvent pas le but car il regarde ʻelement.is_enabled () et element.is_displayed ()`.
En général, la fonction de ʻexpected_conditions` est
python
driver = webdriver.Firefox(executable_path=path, options=options, service_log_path="nul")
driver.get(url)
locator = (By.XPATH, "//div[@id='cat']")
element = WebDriverWait(driver, 30, 1).until(EC.visibility_of_element_located(locator))
À utiliser en combinaison avec WebDriverWait (). Jusqu'à ()
etc.
Regardons cela de plus près.
Expected conditions
Le premier est ʻEC.visibility_of_element_located () `. Cela peut être fait comme suit
python
func = EC.visibility_of_element_located(locator)
element = func(driver)
func
renvoie une fonction qui prend un argument (en supposant driver
).
Ensuite, lorsque vous passez «driver» à «func» et que vous l'exécutez, si l'élément indiqué par «locator» est «affiché», cet élément est renvoyé, et s'il n'est pas «affiché», «False» est renvoyé.
En d'autres termes, func
est une fonction comme find_element (locator)
qui ne lève pas d'exception en cas d'échec.
De plus, find_element ()
peut être modifié comme suit:
python
relativeLocator = (By.XPATH, "./div[@class='meow']") #Chemin relatif
child = element.find_element(*relativeLocator)
Vous pouvez également obtenir l'élément (child
) sous ʻelement. Quand j'essaye de faire quelque chose de similaire avec ʻEC.element_to_be_clickable
, j'obtiens:
python
child = EC.visibility_of_element_located(relativeLocator)(element)
Il semble que d'autres fonctions ʻexpected_conditionspeuvent également obtenir des éléments de chemins relatifs. Cependant, quand je lis l'explication (trouvée), il semble que je suppose le chemin absolu de
driver`.
Regarder la source sur GitHub semble bien, mais je suis un peu inquiet.
Par conséquent, je voudrais fournir d'autres moyens pour traiter des chemins relatifs.
WebDriverWait
Revenons un peu en arrière et regardons WebDriverWait (). Until ()
.
WebDriverWait ()
prend ce qui suit comme arguments (un omis):
-- pilote
: En supposant pilote
--timeout
: temps d'attente maximum
--poll_frequency
: Intervalle d'essai
Et wait.until ()
prend un argument comme décrit ci-dessous.
--method
: Une fonction qui prend un pilote
comme argument. Cette fonction renvoie False
en cas d'échec et non-False` en cas de succès
Si vous écrivez comme suit,
python
timeout = 30
poll_frequency = 1
wait = WebDriverWait(driver, timeout, poll_frequency)
element = wait.until(method)
Le comportement de wait.until (method)
est
method (driver)
réussisse (renvoie autre chose que False
).method (driver)
réussit, sa valeur de retour est renvoyée.Ce sera.
D'après l'explication ci-dessus, si vous écrivez comme suit,
python
locator = (By.XPATH, "//div[@id='cat']")
element = WebDriverWait(driver, 30).until(EC.visibility_of_element_located(locator))
Si l'élément indiqué par «locator» existe et est «affiché», cet élément est affecté à «élément». Si l'élément n'est pas trouvé ou devient "affiché" après 30 secondes, une exception sera levée. Comme vous l'avez peut-être remarqué, vous pouvez obtenir l'élément relatif de ʻelement` en procédant comme suit.
python
relativeLocator = (By.XPATH, "./div[@class='meow']") #Chemin relatif
child = WebDriverWait(element, 30).until(EC.visibility_of_element_located(relativeLocator))
/
disabled`ʻExpected_conditions n'a pas de fonction correspondant à ʻenabled
(disabled
), mais il est facile d'en créer une qui la supporte.
En supposant qu'il est appelé depuis WebDriverWait (). Jusqu'à ()
--Une fonction qui prend un pilote
comme argument. Cette fonction renvoie False
en cas d'échec et non-False` en cas de succès
Vous pouvez voir que vous devez créer la fonction.
Cependant, s'il s'agit d'une fonction, vous ne pouvez passer driver
que si vous utilisez une variable globale, donc vous allez créer Class
.
La façon la plus simple de le faire est la suivante.
python
class IsEnabled():
def __init__(self, locator=(), state=True):
self.locator = locator
self.state = state
def __call__(self, driver):
try:
if self.locator == ():
return False
element = driver.find_element(*self.locator)
return element if element.is_enabled() == self.state else False
except Exception as e:
return False
Il peut être utilisé comme suit:
python
locator = (By.XPATH, "//div[@id='cat']")
element = WebDriverWait(driver, 30, 1).until(IsEnabled(locator))
Il existe deux types de ʻexpected_conditions, l'un qui prend
locator et l'autre qui prend ʻelement
.
Cependant, celui que vous avez créé doit spécifier locator
. Et il ne prend pas en charge les chemins relatifs.
Je vais essayer de l'améliorer afin qu'il puisse gérer ces derniers.
python
class IsEnabled():
def __init__(self, locator=(), element=None, state=True):
self.locator = locator
self.element = element
self.state = state
def __call__(self, driver):
try:
if self.element is not None and self.locator == ():
element = self.element
elif self.element is not None and self.locator != ():
element = self.element.find_element(*self.locator)
elif self.locator != ():
element = driver.find_element(*self.locator)
else:
return False
return element if element.is_enabled() == state else False
except Exception as e:
return False
En faisant cela, vous pouvez l'utiliser comme suit.
python
#Obtenez l'élément indiqué par le localisateur lorsqu'il est activé
element = WebDriverWait(driver, 30, 1).until(IsEnabled(locator=locator))
#Renvoie l'élément lorsque l'élément est activé
element = WebDriverWait(driver, 30, 1).until(IsEnabled(element=element))
#Obtenir lorsque l'élément relatif de l'élément devient activé
child = WebDriverWait(driver, 30, 1).until(IsEnabled(element=element, locator=relativeLocator))
Le «CheckState» affiché au début correspond à un autre état.
find_element ()
Vous pouvez maintenant obtenir l'élément avec la même description, comme find_element ()
.
De plus, vous pouvez l'obtenir en regardant le changement d'état.
Cependant, il est difficile d'écrire ce qui suit à chaque fois, et cela peut être déroutant lorsqu'il est utilisé en combinaison avec find_element ()
.
python
element = WebDriverWait(driver, 30, 1).until(CheckState(element=element, state="clickable"))
Nous définissons donc une fonction wrapper qui change find_element ()
.
Personnellement, il ne semble pas que j'obtienne l'élément du nom de la fonction.
Je pense que c'est aussi un problème que vous créez trop de fonctions de wrapper et que vous ne savez pas ce que vous utilisez.
python
def findElement(driver, locator=(), element=None, state="enabled", must=True, wait=30, interval=0.5, ignore=False):
try:
if element is None and locator == ():
raise ValueError
driverWait = WebDriverWait(driver, wait, interval)
return driverWait.until(CheckState(locator=locator, element=element, state=state))
except TimeoutException:
if must == True and ignore == False:
logger.error(f"findElement: {locator}, {element}, {state}, {must}, {wait}, {interval}, {ignore}")
raise ValueError
return None
except Exception as e:
if ignore == True:
return None
logger.error(f"findElement: {type(e)}, {e}")
raise e
Il est utilisé comme suit.
python
locator = (By.XPATH, "//div[@id='cat']")
element = findElement(driver, locator=locator):
Maintenant, je pense que vous pouvez l'écrire proprement.
Cependant, en fonction du comportement du site, il est difficile de faire divers ajustements.
Même si cela a fonctionné jusqu'à présent, il arrive souvent que le site soit lent et trébuche dans des endroits inattendus.
Après tout, je ne peux pas abandonner time.sleep ()
Il y a une histoire dans laquelle je ne suis pas doué (´ ・ ω ・ `)
Recommended Posts