Selenium仅获取网页部分HTML的原因排查及WebUntis课程元素定位失败问题求助
解决WebUntis时间表元素无法被Selenium定位的问题
我之前爬WebUntis的时候也踩过几乎一模一样的坑,你的问题主要是几个常见的Selenium爬取动态页面的误区导致的,咱们一个个来解决:
1. 复合类名不能直接用By.CLASS_NAME
你要定位的类un-timetable__dnd-avatar un-timetable__dnd-avatar--hidden是两个类名的组合,而By.CLASS_NAME只能匹配单个类,直接用它自然找不到元素。你需要换成CSS选择器或者XPATH来匹配多个类:
用CSS选择器(推荐)
test = driver.find_elements(By.CSS_SELECTOR, ".un-timetable__dnd-avatar.un-timetable__dnd-avatar--hidden")
(注意两个类名之间没有空格,.代表类选择器,连写表示同时匹配这两个类)
用XPATH
test = driver.find_elements(By.XPATH, "//div[contains(@class, 'un-timetable__dnd-avatar') and contains(@class, 'un-timetable__dnd-avatar--hidden')]")
2. 检查是否存在iframe嵌套
WebUntis的核心内容(比如时间表)经常是在iframe里加载的,Selenium默认只会在主页面的DOM里查找元素,不会自动进入iframe。你需要先切换到对应的iframe才能定位到里面的元素:
# 先导入显式等待的工具 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 显式等待iframe加载完成,然后切换 wait = WebDriverWait(driver, 10) iframe = wait.until(EC.presence_of_element_located((By.TAG_NAME, "iframe"))) driver.switch_to.frame(iframe) # 现在再去查找时间表元素 test = driver.find_elements(By.CSS_SELECTOR, ".un-timetable__dnd-avatar.un-timetable__dnd-avatar--hidden") # 操作完iframe内容后,记得切回主页面(如果需要操作其他元素的话) # driver.switch_to.default_content()
3. 替换time.sleep为显式等待,提升稳定性
硬编码的time.sleep非常不稳定——网络慢或者页面渲染延迟的时候,等待时间就不够用了。用WebDriverWait的显式等待,直到目标元素出现/可点击再执行下一步,才是可靠的做法。比如登录、点击按钮这些步骤都可以优化:
# 等待"Mein Stundenplan"按钮可点击再点击 timetable_btn = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[text()='Mein Stundenplan']"))) timetable_btn.click()
4. 优化无头模式配置
无头模式有时候会因为窗口尺寸太小、渲染机制不同导致页面元素加载异常,给Chrome添加这些配置试试:
options = Options() options.add_argument("headless=new") # 新版无头模式,更接近正常浏览器的渲染效果 options.add_argument("window-size=1920,1080") # 设置桌面级窗口大小 options.add_argument("disable-gpu") options.add_argument("enable-javascript") # 确保JavaScript正常启用
整合后的完整代码
把上面的优化点整合到你的代码里,最终版本如下:
import time from bs4 import BeautifulSoup as bs from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 设置浏览器配置 url = "https://klio.webuntis.com" options = Options() options.add_argument("headless=new") options.add_argument("window-size=1920,1080") options.add_argument("disable-gpu") driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) wait = WebDriverWait(driver, 15) # 最长等待15秒 driver.get(url) ################################################################################################### # 进入登录页面 school_parent = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "Select-input"))) school_input = school_parent.find_element(By.XPATH, "./*[1]") school_input.send_keys("<schoolName>") time.sleep(0.6) # 这里短暂等待下拉选项加载 school_input.send_keys(Keys.RETURN) # 登录操作 user_inputs = wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "un-input-group__input"))) for element in user_inputs: if element.get_attribute("type") == "text": element.send_keys("<username>") elif element.get_attribute("type") == "password": element.send_keys("<password>") element.send_keys(Keys.RETURN) # 进入时间表页面 timetable_btn = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[text()='Mein Stundenplan']"))) timetable_btn.click() # 检查并切换到iframe(如果存在) try: iframe = wait.until(EC.presence_of_element_located((By.TAG_NAME, "iframe"))) driver.switch_to.frame(iframe) except: # 如果没有iframe,跳过切换步骤 pass # 查找目标元素 test = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".un-timetable__dnd-avatar.un-timetable__dnd-avatar--hidden"))) print(f"成功找到 {len(test)} 个目标元素") # 切回主页面(如果切换过iframe) driver.switch_to.default_content() # 打印页面源码 this_week = bs(driver.page_source, "html.parser") print(this_week.prettify()) # 关闭浏览器 driver.quit()
内容的提问来源于stack exchange,提问作者Jonas Stefan
相关产品推荐
相关产品推荐

