You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 03:31:37