Selenium操作迪士尼预约页无法自动点击按钮问题咨询
问题根因
你初步排查的Shadow Root方向完全正确。这个迪士尼预约页的所有交互组件都基于Web Components封装,Theme Park Tickets、日期选择按钮都在多层嵌套的Shadow DOM内部,常规从文档根节点发起的XPath、ID、Name定位无法穿透Shadow DOM的封装边界,直接查找必然抛出元素不存在的报错,这就是之前所有定位尝试失败的核心原因。
解决方案
Selenium 4及以上版本原生支持Shadow Root节点操作,不需要注入额外JS,只要逐层获取每一层Shadow Root节点,在对应节点的上下文内查找下一级元素即可,不要直接从driver全局发起查找。
前置环境要求
- Selenium版本≥4.0
- ChromeDriver版本和本地安装的Chrome浏览器大版本完全匹配
可直接运行的修正代码
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from time import sleep def click_shadow_element(driver, root_selector, nested_selectors): """ 逐层穿透Shadow Root查找并点击目标元素 :param driver: webdriver实例 :param root_selector: 最外层非Shadow宿主元素的定位元组 :param nested_selectors: 从第一层Shadow开始,每层宿主/目标元素的定位元组列表 """ current_node = WebDriverWait(driver, 15).until( EC.presence_of_element_located(root_selector) ) # 逐层穿透Shadow边界 for selector in nested_selectors[:-1]: shadow_root = current_node.shadow_root current_node = WebDriverWait(shadow_root, 10).until( EC.presence_of_element_located(selector) ) # 定位最终目标按钮,等待可点击后触发点击 target = WebDriverWait(current_node.shadow_root, 10).until( EC.element_to_be_clickable(nested_selectors[-1]) ) target.click() def main(): while True: options = Options() # 如需后台运行可打开下面的无头配置 # options.add_argument("--headless=new") driver = webdriver.Chrome(options=options) try: driver.get('https://disneyworld.disney.go.com/availability-calendar/?segments=tickets,resort,passholder&defaultSegment=tickets') driver.maximize_window() sleep(7) # 第一步:点击Theme Park Tickets按钮 click_shadow_element( driver, root_selector=(By.TAG_NAME, "app-root"), nested_selectors=[ (By.TAG_NAME, "aw-availability-selector"), (By.TAG_NAME, "dprd-radio-group"), (By.CSS_SELECTOR, "dprd-button:nth-child(1)"), (By.CSS_SELECTOR, "button") ] ) sleep(2) # 第二步:点击July 20日期按钮 calendar_host = WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.TAG_NAME, "aw-availability-calendar")) ) calendar_shadow = calendar_host.shadow_root july20_btn = WebDriverWait(calendar_shadow, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "button[aria-label*='July 20']")) ) july20_btn.click() sleep(3) # 后续检测Disney's Hollywood Studios对勾、发提醒邮件、判断是否循环的逻辑可在此处补充 # 乐园选项同样在对应组件的Shadow Root内,按上述穿透逻辑查找即可 except Exception as e: print(f"本次运行出错: {str(e)}") finally: driver.quit() # 刷新间隔建议不短于15秒,避免被站点反爬拦截 sleep(30) if __name__ == "__main__": main()
关键注意点
- 不要使用从
<html>标签开始的绝对XPath,这类路径层级太深,页面稍有迭代就会失效。Shadow DOM场景下优先用CSS选择器搭配稳定属性(比如组件标签名、aria-label、角色属性)定位,稳定性远高于XPath。 - 不需要用
click_and_hold做长按操作,普通按钮直接调用click()即可,长按反而会触发页面拖拽、文本选中之类的异常交互。 - Shadow Root内的元素不能直接通过driver全局查找,必须先拿到对应元素的宿主节点,获取宿主的
shadow_root属性后,在这个shadow_root上下文里查找内部元素,跨层查找必然失败。 - 循环逻辑里必须在每次循环结束后调用
driver.quit()销毁浏览器实例,否则会出现内存泄漏,运行不了几次程序就会因为内存占满崩溃。 - 后续检测对勾标识时,找到对应Disney's Hollywood Studios选项节点后,判断勾选状态的class是否存在、或者勾选图标元素是否加载出来即可。
内容的提问来源于stack exchange,提问作者Samuel Staggs
相关产品推荐
相关产品推荐

