Je veux gratter toutes les données d'une page implémentée par un défilement infini. Le code python suivant fonctionne.
for i in range(100):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(5)
Cela signifie que chaque fois que je fais défiler l'écran jusqu'en bas, je dois attendre 5 secondes, ce qui est généralement suffisant pour que la page finisse de charger le contenu nouvellement généré. Mais, cela peut ne pas être efficace du temps. La page peut finir de charger le nouveau contenu dans les 5 secondes. Comment savoir si la page a fini de charger le nouveau contenu à chaque défilement? Si je peux détecter cela, je peux faire défiler à nouveau pour voir plus de contenu une fois que je sais que la page a été chargée. C'est plus efficace en temps.
La webdriver
attendra qu'une page se charge par défaut via la méthode .get()
.
Comme vous êtes peut-être à la recherche d'un élément spécifique, comme @ user227215 l'a indiqué, vous devez utiliser WebDriverWait
pour attendre un élément situé dans votre page:
from Selenium import webdriver
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from Selenium.webdriver.common.by import By
from Selenium.common.exceptions import TimeoutException
browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
try:
myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'IdOfMyElement')))
print "Page is ready!"
except TimeoutException:
print "Loading took too much time!"
Je l'ai utilisé pour vérifier les alertes. Vous pouvez utiliser n'importe quelle autre méthode de type pour trouver le localisateur.
EDIT 1:
Je devrais mentionner que la webdriver
attendra le chargement d’une page par défaut. Il n'attend pas le chargement à l'intérieur des cadres ni les requêtes ajax. Cela signifie que lorsque vous utilisez .get('url')
, votre navigateur attendra que la page soit complètement chargée, puis passera à la commande suivante du code. Mais lorsque vous publiez une demande ajax, webdriver
n'attend pas et il vous incombe d'attendre le délai approprié pendant le chargement de la page ou d'une partie de la page. il existe donc un module nommé expected_conditions
.
Essayer de passer find_element_by_id
au constructeur pour presence_of_element_located
(comme indiqué dans le réponse acceptée ) a provoqué le déclenchement de NoSuchElementException
. Je devais utiliser la syntaxe dans fragles ' comment :
from Selenium import webdriver
from Selenium.common.exceptions import TimeoutException
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from Selenium.webdriver.common.by import By
driver = webdriver.Firefox()
driver.get('url')
timeout = 5
try:
element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)
except TimeoutException:
print "Timed out waiting for page to load"
Cela correspond à exemple dans la documentation . Voici un lien vers le documentation pour By .
Trouvez ci-dessous 3 méthodes:
Vérification de la page readyState (non fiable):
def page_has_loaded(self):
self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
page_state = self.driver.execute_script('return document.readyState;')
return page_state == 'complete'
La fonction d'assistance
wait_for
est bonne, mais malheureusementclick_through_to_new_page
est ouverte à la situation de concurrence critique dans laquelle nous parvenons à exécuter le script dans l'ancienne page avant que le navigateur n'ait commencé à traiter le clic etpage_has_loaded
revient juste vrai tout de suite.
id
Comparer les nouveaux identifiants de page avec l'ancien:
def page_has_loaded_id(self):
self.log.info("Checking if {} page is loaded.".format(self.driver.current_url))
try:
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id
except NoSuchElementException:
return False
Il est possible que la comparaison d'identifiants ne soit pas aussi efficace que d'attendre des exceptions de références obsolètes.
staleness_of
Utilisation de la méthode staleness_of
:
@contextlib.contextmanager
def wait_for_page_load(self, timeout=10):
self.log.debug("Waiting for page to load at {}.".format(self.driver.current_url))
old_page = self.find_element_by_tag_name('html')
yield
WebDriverWait(self, timeout).until(staleness_of(old_page))
Pour plus de détails, consultez le blog de Harry .
De Selenium/webdriver/support/wait.py
driver = ...
from Selenium.webdriver.support.wait import WebDriverWait
element = WebDriverWait(driver, 10).until(
lambda x: x.find_element_by_id("someId"))
Comme mentionné dans le réponse de David Cullen , j'ai toujours recommandé d'utiliser une ligne comme celle-ci:
_element_present = EC.presence_of_element_located((By.ID, 'element_id'))
WebDriverWait(driver, timeout).until(element_present)
_
Il m’était difficile pour moi de trouver n’importe où tous les localisateurs possibles pouvant être utilisés avec la syntaxe By
; j’ai donc pensé qu’il serait utile de fournir ici la liste. Selon Web Scraping with Python de Ryan Mitchell:
ID
Utilisé dans l'exemple; trouve des éléments par leur attribut HTML id
CLASS_NAME
Utilisé pour rechercher des éléments par leur attribut de classe HTML. Pourquoi cette fonction _
CLASS_NAME
_ n'est-elle pas simplementCLASS
? L'utilisation du formulaire _object.CLASS
_ créerait des problèmes pour la bibliothèque Java de Selenium, où _.class
_ est une méthode réservée. Afin de conserver la syntaxe Selenium cohérente entre différentes langues, nous avons utilisé _CLASS_NAME
_.
CSS_SELECTOR
Recherchez des éléments par leur classe, leur identifiant ou leur nom de balise, à l'aide de la convention _
#idName
_, _.className
_,tagName
.
LINK_TEXT
Trouve les balises HTML d'après le texte qu'elles contiennent. Par exemple, un lien indiquant "Suivant" peut être sélectionné à l'aide de _
(By.LINK_TEXT, "Next")
_.
PARTIAL_LINK_TEXT
Similaire à _
LINK_TEXT
_, mais correspond à une chaîne partielle.
NAME
Trouve les balises HTML par leur attribut name. C'est pratique pour les formulaires HTML.
TAG_NAME
Affine les balises HTML par leur nom.
XPATH
Utilise une expression XPath ... pour sélectionner les éléments correspondants.
Sur une note de côté, au lieu de faire défiler 100 fois vers le bas, vous pouvez vérifier s'il n'y a plus de modifications dans le DOM (nous sommes dans le cas du bas de la page étant AJAX chargé paresseux)
def scrollDown(driver, value):
driver.execute_script("window.scrollBy(0,"+str(value)+")")
# Scroll down the page
def scrollDownAllTheWay(driver):
old_page = driver.page_source
while True:
logging.debug("Scrolling loop")
for i in range(2):
scrollDown(driver, 500)
time.sleep(2)
new_page = driver.page_source
if new_page != old_page:
old_page = new_page
else:
break
return True
Pourquoi ne pas mettre WebDriverWait dans While en boucle et intercepter les exceptions.
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
browser = webdriver.Firefox()
browser.get("url")
delay = 3 # seconds
while True:
try:
WebDriverWait(browser, delay).until(EC.presence_of_element_located(browser.find_element_by_id('IdOfMyElement')))
print "Page is ready!"
break # it will break from the loop once the specific element will be present.
except TimeoutException:
print "Loading took too much time!-Try again"
Ici je l'ai fait en utilisant un formulaire plutôt simple:
from Selenium import webdriver
browser = webdriver.Firefox()
browser.get("url")
searchTxt=''
while not searchTxt:
try:
searchTxt=browser.find_element_by_name('NAME OF ELEMENT')
searchTxt.send_keys("USERNAME")
except:continue
Avez-vous essayé driver.implicitly_wait
. C'est comme un paramètre pour le pilote. Vous ne l'appelez donc qu'une fois dans la session. Il indique au pilote d'attendre le temps imparti jusqu'à ce que chaque commande puisse être exécutée.
driver = webdriver.Chrome()
driver.implicitly_Wait(10)
Par conséquent, si vous définissez un délai d’attente de 10 secondes, la commande sera exécutée dès que possible et attendra 10 secondes avant d’abandonner. J'ai utilisé cela dans des scénarios de défilement similaires, je ne vois donc pas pourquoi cela ne fonctionnerait pas dans votre cas. J'espère que c'est utile.