#python #selenium
Вопрос:
У меня есть небольшой скрипт selenium, который заходит на веб-сайт, проверяет некоторые вещи и выполняет некоторые шаги. Проблема, с которой я сталкиваюсь, заключается в следующем. Идентификатор «s2id_ddlHotels» иногда меняется. Бот найдет «s2id_ddlHotels» и завершит действия. Но при следующем запуске он загрузит этот элемент «s2id_ddlHotelsWithDate». До сих пор я видел только между этими двумя элементами. Как я могу с этим справиться? Своего рода указывает коду python сначала искать «s2id_ddlHotels», если он найден, выполните другие действия. и если это невозможно найти, найдите «s2id_ddlHotelsWithDate» и выполните следующие действия? Я считаю, что здесь мне нужно использовать оператор IF, но я не уверен, как его правильно реализовать.
Мой текущий код:
try:
time.sleep(5)
driver.find_element_by_id("s2id_ddlHotels").click() #issue here
time.sleep(2)
driver.find_element_by_id('s2id_autogen1_search').send_keys(hotelName)
time.sleep(2)
driver.find_element_by_id('s2id_autogen1_search').send_keys(Keys.RETURN)
time.sleep(2)
driver.find_element_by_name("fileRoomIncomeName").send_keys(fileName)
time.sleep(2)
element = driver.find_element_by_class_name("fileupload-preview")
driver.execute_script(f"arguments[0].innerText = '{fileName}'", element)
except Exception as e:
pass
#print(e)
Комментарии:
1. Почему вы не можете использовать
contains
?2. Я пытаюсь нажать на выпадающее меню, поэтому не думаю, что текст будет работать правильно?
3. Так и должно быть. Можете ли вы опубликовать HTML раскрывающегося списка?
4. jsfiddle.net/fryx7wh6/3 Это html-код. Это немного грязно. Но если я смогу найти идентификатор, просто указав «s2id_ddlHotels», все должно быть в порядке. Потому что есть два удостоверения личности. Иногда «s2id_ddlHotels», а иногда «s2id_ddlHotelsWithDate»
5. Допустим, если
s2id_ddlHotels
он недоступен, вы не получите исключения «Элемент не найден», тогда вы хотите найтиs2id_ddlHotelsWithDate
элемент. Это правильно?
Ответ №1:
Вам просто нужно использовать систему ожидания ожидаемого состояния Selenium. Это позволяет безопасно дождаться элемента с в нужное время. Это станет еще проще в управлении, если вы начнете оборачивать его, как это сделал я.
Ниже приведен элементарный пример того, что вы ищете. Это не идеально, но достаточно, чтобы вы начали.
import os
import sys
from typing import Union
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.remote.webelement import WebElement
# We are assuming the webdriver is right next to this file
THIS_FILE = os.path.abspath(__file__)
THIS_DIR = os.path.dirname(THIS_FILE)
# I am on linux so no *.exe file
CHROME_DRIVER_PATH = os.path.join(THIS_DIR, 'chromedriver')
# Get the driver
driver = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH)
def get_element_by_locator(the_driver, locator: str, timeout: int = 10, by=By.ID) -> Union[None, WebElement]:
"""Explicit waits by Selenium: https://selenium-python.readthedocs.io/waits.html#explicit-waits
:param the_driver: The instantiated driver you created
:param locator: the string locator value
:param timeout: Default wait time before giving up
:param by: What is the locator searching By? is it an ID, XPATH, CLASSNAME?
:return: None if not found else the found WebElement
"""
try:
return WebDriverWait(the_driver, timeout).until(
expected_conditions.presence_of_element_located((by, locator))
)
except TimeoutException:
# We could not find the element matching matched the locator
# By returning None we can make an educated decision about what to do next
return None
# Notice I don't have to sleep by 5 seconds anymore.
# Selenium will search for the element for up to 5 seconds before giving up.
# In that case you are going to get None or the desired element.
# Maybe you can set it to 60 seconds and if it finds it with in 6 seconds
# you can still safely move on. Worst case you waited 60 seconds and then got None.
hotels_button = get_element_by_locator(driver, 's2id_ddlHotels', timeout=5, by=By.ID)
if hotels_button is not None:
print('Happy times the page is in the desired state')
hotels_button.click()
else:
print('I did not find my element, no point in continuing')
print('I can attempt to recover, reload the page or just quit')
sys.exit(1)
# If you notice we only checked if the first element existed.
# Every thing below here in theory could be None also.
hotels_search_input = get_element_by_locator(driver, 's2id_autogen1_search', timeout=2, by=By.ID)
hotels_search_input.send_keys('hotelName')
hotels_search_input.send_keys(Keys.RETURN)
file_name = 'my_file'
file_name_input = get_element_by_locator(driver, 'fileRoomIncomeName', timeout=2, by=By.NAME)
file_name_input.send_keys(file_name)
file_upload_preview = get_element_by_locator(driver, "fileupload-preview", timeout=2, by=By.CLASS_NAME)
driver.execute_script(f"arguments[0].innerText = '{file_name}'", file_upload_preview)