如何让Selenium窗口会话中的JavaScript变量保持持久化?
问题原因
页面刷新、导航跳转或触发页面重载时,浏览器会重置整个window上下文,你之前通过JavaScript Executor注入的变量和XHR Hook都会被清除,因此window.processFlag会变成undefined。
解决方案
方案1:利用Chrome DevTools Protocol(CDP)自动注入Hook代码
这是最可靠的方案,CDP的Page.addScriptToEvaluateOnNewDocument会让你的Hook代码在每个新文档加载的最早期阶段自动执行,覆盖页面刷新、跳转等所有场景,确保XHR Hook和变量始终生效。
Python Selenium示例代码:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait options = Options() driver = webdriver.Chrome(options=options) # 定义Hook脚本 hook_script = """ window.processFlag = -1; var open = window.XMLHttpRequest.prototype.open, send = window.XMLHttpRequest.prototype.send; function openReplacement(method,url,async,user,password) { this._url = url; return open.apply(this,arguments); } function sendReplacement(data){ if(this.onreadystatechange){ this._onreadystatechange = this.onreadystatechange; } this.onreadystatechange = onReadyStateChangeReplacement; window.processFlag = 0; return send.apply(this,arguments); } function onReadyStateChangeReplacement(){ window.processFlag = this.readyState; if(this._onreadystatechange){ return this._onreadystatechange.apply(this,arguments); } } window.XMLHttpRequest.prototype.open = openReplacement; window.XMLHttpRequest.prototype.send = sendReplacement; """ # 通过CDP设置页面加载时自动执行脚本 driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": hook_script}) # 访问目标页面 driver.get("your_target_url") # 自定义WebDriverWait判断逻辑 def check_process_flag(driver): return driver.execute_script("return window.processFlag === 4;") WebDriverWait(driver, 10).until(check_process_flag)
方案2:使用sessionStorage持久化状态
sessionStorage会在同一窗口会话的页面刷新、跳转后保留(关闭窗口才会清除),可以把processFlag存储在这里,避免页面刷新后丢失状态。需要修改Hook脚本的变量读写逻辑:
修改后的Hook脚本:
// 初始化sessionStorage中的状态,不存在则设为-1 if (!sessionStorage.getItem('processFlag')) { sessionStorage.setItem('processFlag', '-1'); } var open = window.XMLHttpRequest.prototype.open, send = window.XMLHttpRequest.prototype.send; function openReplacement(method,url,async,user,password) { this._url = url; return open.apply(this,arguments); } function sendReplacement(data){ if(this.onreadystatechange){ this._onreadystatechange = this.onreadystatechange; } this.onreadystatechange = onReadyStateChangeReplacement; sessionStorage.setItem('processFlag', '0'); return send.apply(this,arguments); } function onReadyStateChangeReplacement(){ sessionStorage.setItem('processFlag', String(this.readyState)); if(this._onreadystatechange){ return this._onreadystatechange.apply(this,arguments); } } window.XMLHttpRequest.prototype.open = openReplacement; window.XMLHttpRequest.prototype.send = sendReplacement;
对应的WebDriverWait判断逻辑:
def check_process_flag(driver): flag = driver.execute_script("return sessionStorage.getItem('processFlag');") return int(flag) == 4 WebDriverWait(driver, 10).until(check_process_flag)
注意:此方案需要配合自动注入脚本(比如方案1的CDP方法),否则页面刷新后XHR Hook会失效,仅状态保留无意义。
方案3:页面刷新后手动重新注入脚本
如果无法使用CDP,可在每次页面刷新、导航或触发重载后,手动重新注入Hook脚本:
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By def inject_hook(driver): hook_script = """ window.processFlag = -1; var open = window.XMLHttpRequest.prototype.open, send = window.XMLHttpRequest.prototype.send; // 剩余Hook代码与原脚本一致 """ driver.execute_script(hook_script) # 首次加载页面并注入 driver.get("your_target_url") inject_hook(driver) # 刷新页面后重新注入 driver.refresh() inject_hook(driver) # 点击元素触发页面重载后,等待页面加载完成再注入 driver.find_element(By.ID, "trigger-refresh-btn").click() WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.TAG_NAME, "body"))) inject_hook(driver)
此方案缺点是容易遗漏页面重载场景,需额外处理各种可能触发刷新的操作,稳定性不如方案1。
内容的提问来源于stack exchange,提问作者vijay eathakotla
相关产品推荐
相关产品推荐

