Je me bats avec un comportement inattendu dans un racleur Web Python 3 basé sur Selenium et je veux comprendre ce qui se passe:
J'analyse les sites proposant des offres d'emploi. Après la recherche initiale, j'obtiens 1 à n sites de résultats. Ce nombre de sites est indiqué sur la toute première page en tant que partie texte de l'élément "m-pagination__meta" et est affiché en allemand, par ex. "1 sur 48". J'ai besoin de cette chaîne pour un traitement ultérieur. Il se trouve sur le site, il ne fait PAS partie d'une iframe.
Exemple de lien du site Web de l'emploi
Le HTML:
wait = WebDriverWait(self.driver, 10) wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME,"m-pagination__meta"), "1 von 48")) pagesTotal = int(self.driver.find_element_by_class_name("m-pagination__meta").text.split(" ")[2])
Maintenant vient la partie bizarre: Quand je débogue le programme et essayez d'accéder à l'élément string directement avec "m-pagination__meta" .text il renvoie une chaîne vide.
Pourtant, quand j'accède à l'objet élément mère m-pagination__meta et l'inspecte avec le débogueur, en faisant défiler vers le bas à la propriété de texte, la chaîne attendue "1 sur 48" est là. Après cette inspection, je PEUX accéder au texte "m-pagination__meta" avec les résultats attendus.
Ce comportement ne semble pas dépendre du timing. J'ai essayé d'attendre la présence de l'élément requis avec un code comme
<div class="m-pagination"> <div class="m-pagination__inner m-pagination__inner--borderBottom"> <button class="m-pagination__button m-pagination__button--disabled" data-page="" data-event-action="click: pagination-first"> <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large "> <g fill="none" stroke="currentColor" stroke-width=".7" stroke-linecap="round" stroke-linejoin="round"> <path d="M9 13.2L4.2 8.5 9 3.8"></path> <path d="M12.8 13.2L8 8.5l4.7-4.7"></path> </g> </svg> </button> <button class="m-pagination__button m-pagination__button--previous m-pagination__button--disabled" data-page="false" data-event-action="click: pagination-previous"> <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large "> <path fill="none" stroke="currentColor" stroke-width=".8" stroke-linecap="round" stroke-linejoin="round" d="M10.9 3.8L6 8.6l4.7 4.6"></path> </svg> </button> <span class="m-pagination__meta" data-number="1"> 1 von 43 </span> <button class="m-pagination__button m-pagination__button--next m-pagination__button--available" data-page="2" data-event-action="click: pagination-next"> <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large "> <path fill="none" stroke="currentColor" stroke-width=".7" stroke-linecap="round" stroke-linejoin="round" d="M6.1 3.8L11 8.6l-4.7 4.6"></path> </svg> </button> </div> </div>
en vain (bien sûr, j'ai réalisé qu'il était stupide de rechercher une chaîne spécifique quand je ne le fais pas. Je ne sais pas lequel il donnera, mais je ne savais pas comment le coder autrement.)
J'ai aussi essayé des attentes "normales" en utilisant sleep, mais rien ne semble fonctionner, seule l'inspection mentionnée dans le débogueur, qui est inutile à des fins de production.
J'aimerais vraiment comprendre ce qui se passe ici.
3 Réponses :
Le problème peut être dû à l'élément apparaissant dans la source HTML lors du chargement de la page, mais JavaScript remplit la valeur en arrière-plan.
Vous pouvez utiliser une expression régulière dans XPath pour faire correspondre le texte en fonction d'un modèle :
xpath = '//*[contains(@class, "m-pagination__meta") and matches(normalize-space(text()), "\d+ von \d+")]' wait = WebDriverWait(self.driver, 30) wait.until(EC.element_to_be_present(By.XPATH, xpath))
Remarque: peut-être augmenter la période d'attente à 30 secondes également, juste pour être sûr.
Vous semblez être assez proche de WebDriverWait . Mais malheureusement, l'élément est situé tout en bas de l ' arborescence DOM et n'est pas dans l' Viewport . Par conséquent, une chaîne vide est renvoyée.
La solution serait de scroll ()
l'élément dans la Viewport une fois que l'élément est visible dans le HTML DOM a> en utilisant EC comme visibilité_of_element_located ()
, puis vous pouvez essayer d'extraire le texte souhaité et vous pouvez utiliser l'un des éléments suivants Stratégies de localisation :
Utilisation de CSS_SELECTOR
:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC
Utilisation de XPATH
:
driver.execute_script("return arguments[0].scrollIntoView(true);", WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//span[@class='m-pagination__meta']")))) print(driver.find_element_by_xpath("//span[@class='m-pagination__meta']").get_attribute("innerHTML"))
Remarque : vous devez ajouter les importations suivantes:
driver.execute_script("return arguments[0].scrollIntoView(true);", WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.m-pagination__meta")))) print(driver.find_element_by_css_selector("span.m-pagination__meta").get_attribute("innerHTML"))
Merci beaucoup pour les extraits et les explications! J'ai marqué la réponse ci-dessous comme correcte, simplement parce que c'était la seule où le code fonctionnait hors de la boîte. C'était difficile à décider, dommage qu'une seule réponse puisse être considérée comme correcte.
Il y a une barre de défilement verticale
présente qui divise la page en deux sections.Cependant, vous devez d'abord trouver l'élément de la barre de défilement de gauche, puis faire location_once_scrolled_into_view
.Une fois que vous portée que vous pouvez identifier l'élément que vous recherchez.
Essayez le code ci-dessous.
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver=webdriver.Chrome() driver.get('https://www.karriere.at/jobs/programmierer/wien') WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='c-jobsSearch__listing']"))).location_once_scrolled_into_view print(driver.find_element_by_xpath("//span[@class='m-pagination__meta']").get_attribute('innerText'))
Merci beaucoup! Ce code a fonctionné immédiatement.
Pouvez-vous publier un extrait du code HTML contenant le texte que vous recherchez?
Vous savez quoi, il semble que la page se charge lorsque vous faites défiler vers le bas. C'est pourquoi Selenium ne trouve rien tant que vous n'exécutez pas le débogueur. Dans le débogueur, vous faites défiler jusqu'à l'élément, ce qui entraîne le chargement du reste de la page, puis Selenium peut trouver l'élément.
Non, l'élément est là sans défilement. Je viens de vérifier ...