Selenium Python:点击跳转按钮时在DOM变更前获取变量
问题
我一直在尝试在Selenium Python启动的浏览器会话中实现页面刷新和跨页面变量持久化,但测试后发现localStorage、sessionStorage或window.name都无法生效。
于是我改用Python脚本,通过重复执行driver.execute_script('return variable')来收集被点击元素的数据——我给页面添加了点击事件监听器,把点击元素的值存到页面本地变量里。
这种方法大部分情况正常,但点击带跳转链接、会改变DOM的按钮时就失效了。我猜测是点击事件、变量存储、变量获取和页面跳转几乎同时发生,DOM变更早于变量获取,导致数据没被收集到。
相关代码如下:
from selenium.common import TimeoutException, WebDriverException from selenium.webdriver.support.ui import WebDriverWait from selenium import webdriver class Main: def __init__(self, page_url): self.__driver = webdriver.Chrome() self.__element_list = [] self.__page_url = page_url def start(self): program_return = [] self.__driver.get(self.__page_url) event_js = ''' var array_events = [] var registerOuterHtml = (e) => { array_events.push(e.target.outerHTML) window.array_events = array_events } var registerUrl = (e) => { array_events.push(document.documentElement.outerHTML) } getElementHtml = document.addEventListener("click", registerOuterHtml, true) getDOMHtml = document.addEventListener("click", registerUrl, true) ''' return_js = '''return window.array_events''' self.__driver.set_script_timeout(10000) self.__driver.execute_script(event_js) try: for _ in range(800): if array_events := self.__driver.execute_script(return_js): if array_events[-2:] not in program_return: program_return.append(array_events[-2:]) else: try: WebDriverWait(self.__driver, 0.1).until( lambda driver: self.__driver.current_url != self.__page_url) except TimeoutException: pass else: self.__page_url = self.__driver.current_url self.__driver.execute_script(event_js) except WebDriverException: pass finally: print(len(program_return)) # should print total number of clicks made.
测试方式:
Main('any url you wish').start()
点击页面元素(至少包含一个跳转页面的按钮)后,手动关闭窗口查看结果。
假设跨页面变量持久化不可行,请问如何在同一点击动作中,在页面变更前获取点击时设置的变量值?是否可以延迟页面跳转?
解决方案
1. 阻止默认跳转并延迟执行
修改点击事件监听器,先拦截跳转行为,等Python端获取到数据后再执行跳转。
更新event_js代码:
var array_events = [] var pendingNavigation = null; var registerOuterHtml = (e) => { array_events.push(e.target.outerHTML) window.array_events = array_events // 定位触发跳转的元素 let target = e.target; while (target && !target.href) { target = target.parentNode; } if (target && target.href) { e.preventDefault(); // 阻止默认跳转 pendingNavigation = target.href; // 保存跳转地址 } } var registerUrl = (e) => { array_events.push(document.documentElement.outerHTML) } document.addEventListener("click", registerOuterHtml, true) document.addEventListener("click", registerUrl, true) // 新增方法供Python调用执行跳转 window.executePendingNavigation = () => { if (pendingNavigation) { window.location.href = pendingNavigation; pendingNavigation = null; } }
然后在Python循环中获取数据后,触发跳转:
if array_events := self.__driver.execute_script(return_js): if array_events[-2:] not in program_return: program_return.append(array_events[-2:]) # 执行待处理的跳转 self.__driver.execute_script('window.executePendingNavigation()')
2. 用异步脚本获取数据
利用Selenium的execute_async_script让JS主动通知Python端数据已就绪,避免轮询的时间差问题。
替换Python中的循环逻辑:
try: while True: # 异步等待JS返回点击数据 new_events = self.__driver.execute_async_script(''' var callback = arguments[0]; // 已有数据直接返回并清空 if (window.array_events && window.array_events.length > 0) { callback(window.array_events); window.array_events = []; } else { // 监听点击事件,有数据立即返回 var sendData = function() { callback(window.array_events); window.array_events = []; document.removeEventListener("click", sendData, true); } document.addEventListener("click", sendData, true); } ''') if new_events: if new_events[-2:] not in program_return: program_return.append(new_events[-2:]) # 检查页面是否跳转 try: WebDriverWait(self.__driver, 0.1).until( lambda driver: driver.current_url != self.__page_url) except TimeoutException: pass else: self.__page_url = self.__driver.current_url self.__driver.execute_script(event_js) # 重新注册事件 except WebDriverException: pass
3. 页面卸载前兜底获取数据
通过beforeunload事件在页面即将卸载时保存数据,确保Python能在跳转前拿到最后一批数据。
更新event_js添加卸载监听:
window.addEventListener('beforeunload', function(e) { // 把当前数据挂载到window,供Python获取 window.pendingData = window.array_events; });
在Python端检测到页面跳转时,先获取兜底数据:
else: // 获取页面卸载前的最后数据 pending_data = self.__driver.execute_script('return window.pendingData || []') if pending_data: if pending_data[-2:] not in program_return: program_return.append(pending_data[-2:]) self.__page_url = self.__driver.current_url self.__driver.execute_script(event_js)
内容的提问来源于stack exchange,提问作者RifloSnake
相关产品推荐
相关产品推荐

