如何在Selenium遍历餐厅餐型时无需重启Driver避免失效元素错误
解决Selenium遍历餐厅餐型时无需重启浏览器的方案
需求是遍历斯坦福大学餐厅菜单页面的所有餐厅及对应餐型,控制台输出选中的餐厅和餐型,目前代码通过每次循环重启WebDriver来避免失效元素错误,但效率低下,希望找到无需重启浏览器的解决方法。
原代码
import time import csv from datetime import datetime from pytz import timezone from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from dining_info import * # Setup timezones for PST to ensure continuity pst_timezone = timezone('America/Los_Angeles') pst_now = datetime.now(pst_timezone) # SETUP VARIABLES FOR TEMPORARY TESTING todays_date = pst_now.strftime('%m/%d/%Y').lstrip("0").replace("/0", "/") meal_type = "Breakfast" dining_hall = "Arrillaga" # Set up Chrome options chrome_options = webdriver.ChromeOptions() # Uncomment the next line to run in headless mode # chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-extensions') # Set up WebDriver using WebDriver Manager service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service, options=chrome_options) url = 'https://rdeapps.stanford.edu/dininghallmenu/' driver.get(url) # Wait for the date dropdown to be present date_dropdown = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, 'MainContent_lstDay')) ) # Retrieve currently selected date selected_date = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#MainContent_lstDay option[selected="selected"]')) ).get_attribute('value') # Convert selected date format to match today's date format selected_date_converted = selected_date.split(' - ')[0] print("Dates match:", selected_date_converted == todays_date) # ------------------------------------------------------------------------------------------------- # for hall in dining_hall_list: # Wait for the dining hall dropdown to be present dining_hall_dropdown = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, 'MainContent_lstLocations')) ) # Select dining_hall from the dining hall dropdown Select(dining_hall_dropdown).select_by_value(hall) # Retrieve currently selected dining hall selected_dining_hall = Select(dining_hall_dropdown).first_selected_option.get_attribute('value') print("Selected dining hall:", dining_hall_alias[selected_dining_hall]) for meal in meal_type_list: # Wait for the meal type dropdown to be present meal_type_dropdown = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, 'MainContent_lstMealType')) ) # Select meal_type from the meal type dropdown Select(meal_type_dropdown).select_by_value(meal) # Retrieve currently selected meal type // using this method because stale element when trying method in line 61 selected_meal_type = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#MainContent_lstMealType option[selected="selected"]')) ).get_attribute('value') print("Selected meal type:", selected_meal_type) # Close instance of WebDriver and reinstate to stop stale element driver.quit() time.sleep(0.5) driver = webdriver.Chrome(service=service, options=chrome_options) driver.get(url) # ------------------------------------------------------------------------------------------------- # # Wait for user input before closing the browser input("Press enter to close the browser...") driver.quit()
核心问题分析
失效元素错误(StaleElementReferenceException)的根源是:选择餐厅后页面会局部刷新,之前获取的下拉框元素引用失效。重启浏览器是极端解决方案,通过以下调整可以避免:
具体修改步骤
- 移除循环内的WebDriver重启代码:直接删除
driver.quit()、重新初始化和driver.get(url)的代码,全程复用同一个浏览器实例。 - 修正代码缩进:原代码中
selected_dining_hall和for meal循环的缩进错误,会导致语法执行异常,先统一修正缩进。 - 优化等待条件:将
EC.presence_of_element_located替换为EC.element_to_be_clickable,确保元素不仅存在还能交互,避免过早操作。 - 选择餐厅后等待页面更新:选中餐厅后,等待餐型下拉框重新加载完成(比如等待其可交互),确保页面局部刷新完成后再操作餐型。
修改后的完整代码
import time import csv from datetime import datetime from pytz import timezone from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from dining_info import * # Setup timezones for PST to ensure continuity pst_timezone = timezone('America/Los_Angeles') pst_now = datetime.now(pst_timezone) # SETUP VARIABLES FOR TEMPORARY TESTING todays_date = pst_now.strftime('%m/%d/%Y').lstrip("0").replace("/0", "/") meal_type = "Breakfast" dining_hall = "Arrillaga" # Set up Chrome options chrome_options = webdriver.ChromeOptions() # Uncomment the next line to run in headless mode # chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-extensions') # Set up WebDriver using WebDriver Manager service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service, options=chrome_options) url = 'https://rdeapps.stanford.edu/dininghallmenu/' driver.get(url) # Wait for the date dropdown to be clickable (更可靠的等待条件) date_dropdown = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, 'MainContent_lstDay')) ) # Retrieve currently selected date selected_date = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#MainContent_lstDay option[selected="selected"]')) ).get_attribute('value') # Convert selected date format to match today's date format selected_date_converted = selected_date.split(' - ')[0] print("Dates match:", selected_date_converted == todays_date) # ------------------------------------------------------------------------------------------------- # for hall in dining_hall_list: # 每次循环重新获取餐厅下拉框,避免引用失效 dining_hall_dropdown = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, 'MainContent_lstLocations')) ) # Select dining_hall from the dining hall dropdown Select(dining_hall_dropdown).select_by_value(hall) # 等待页面局部刷新完成:等待餐型下拉框可交互 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, 'MainContent_lstMealType')) ) # Retrieve currently selected dining hall selected_dining_hall = Select(dining_hall_dropdown).first_selected_option.get_attribute('value') print("Selected dining hall:", dining_hall_alias[selected_dining_hall]) for meal in meal_type_list: # 每次循环重新获取餐型下拉框 meal_type_dropdown = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, 'MainContent_lstMealType')) ) # Select meal_type from the meal type dropdown Select(meal_type_dropdown).select_by_value(meal) # Retrieve currently selected meal type selected_meal_type = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#MainContent_lstMealType option[selected="selected"]')) ).get_attribute('value') print("Selected meal type:", selected_meal_type) # 可选:短暂等待,避免操作过快触发页面异常 time.sleep(0.2) # ------------------------------------------------------------------------------------------------- # # Wait for user input before closing the browser input("Press enter to close the browser...") driver.quit()
关键改动说明
- 全程复用同一个WebDriver实例,无需重启,大幅提升效率。
- 每次操作下拉框前都重新获取元素并等待可交互,彻底避免失效元素引用。
- 选中餐厅后增加餐型下拉框的等待,确保页面局部刷新完成后再进行下一步操作。
- 修正了原代码的缩进错误,保证语法正确执行。
内容的提问来源于stack exchange,提问作者Student_102345
相关产品推荐
相关产品推荐

