当前位置: 首页 > news >正文

Selenium进阶:动作链、窗口切换与元素等待实战指南

1. 项目概述:从基础定位到高级交互的跨越

如果你已经用Selenium写过一些简单的自动化脚本,比如自动登录、填写表单或者抓取静态数据,那你肯定已经体会到了它的便利。但当你开始面对更真实的网页环境时,很快就会发现,光会find_elementclick是远远不够的。页面元素不会总是乖乖地立在那里等你点击,一个链接点击后可能弹出一个新窗口,而你需要的数据可能在你滚动到页面底部后才懒洋洋地加载出来。这就是“Selenium进阶”要解决的核心问题:如何让你的自动化脚本像真人一样,从容应对网页的动态性、复杂交互和多任务场景。今天,我们就深入聊聊动作链、窗口切换和元素等待这三个构建健壮、智能自动化脚本的基石技术。掌握了它们,你才能说自己真正跨过了Selenium的门槛,能够处理90%以上的真实网页自动化场景,无论是复杂的Web应用测试,还是需要处理交互的动态数据爬取。

2. 核心需求与场景解析

2.1 为什么基础API不够用?

在入门阶段,我们通常使用Selenium提供的基础命令,如click()send_keys()。这些命令在简单场景下工作良好,但它们本质上是“原子操作”,模拟的是最基础的浏览器事件。当遇到需要连续操作(如拖拽)、悬停触发下拉菜单,或者页面响应速度不确定时,这些原子操作就显得力不从心了。例如,一个简单的click()可能无法触发一个由JavaScript监听复杂鼠标事件(如mousedown->mousemove->mouseup)才能激活的拖拽功能。再比如,你点击一个按钮后,如果脚本立刻去新窗口里找元素,而新窗口还在加载中,那么NoSuchElementException就会让你的脚本戛然而止。因此,进阶的需求源于对操作真实性脚本健壮性的追求。

2.2 三大核心技术的应用场景

让我们把这三个技术点放到具体的场景里看,你就明白它们为何是“进阶”必备了。

动作链:想象一下测试一个在线绘图工具。你需要模拟用户画一条直线,这涉及点击画布某点、按住鼠标、拖动到另一点、然后松开。或者测试一个电商网站,需要将商品拖入购物车。这些连续的、复合的鼠标或键盘操作,就是动作链的用武之地。它通过ActionChains类将多个操作链接起来,然后一次性执行,完美模拟了人类的连续交互行为。

窗口切换:这是处理多标签页或弹窗的利器。典型场景包括:点击一个“在新窗口打开”的链接;提交表单后弹出一个提示框;进行OAuth授权登录时跳转到第三方网站。你的脚本必须能够识别出新窗口,并将操作上下文切换到新窗口,完成任务后还可能需切回原窗口。如果处理不当,你的所有后续命令都会发向一个已经不可见或已关闭的窗口,导致脚本失败。

元素等待:这是编写稳定脚本的生命线。现代网页大量使用Ajax、React、Vue等前端框架,元素的出现、消失、更新往往是异步的。如果你在元素尚未出现时就尝试点击它,脚本必然失败。元素等待机制就是告诉Selenium:“耐心点,等这个元素满足某个条件(如出现、可点击、可见)后再执行下一步。”这彻底消除了因网络速度、服务器响应慢或前端渲染导致的随机性失败。

将这三点结合起来,一个完整的进阶场景可能是:自动化测试一个文件上传流程。脚本需要:1) 将鼠标移动到“上传”按钮上悬停以显示隐藏的菜单(动作链);2) 点击菜单中的“从云盘选择”,这会触发一个新窗口打开(窗口切换);3) 在新窗口中,等待云盘文件列表加载完成(元素等待),然后选择文件。你看,一个看似简单的功能,背后就需要这三项技术的协同工作。

3. 动作链详解:模拟精细的人机交互

3.1 ActionChains 的核心原理与基本结构

Selenium的ActionChains类并不是简单地串行发送多个click命令。它的设计基于W3C的WebDriver协议中关于“动作”的规范,旨在模拟底层输入设备(鼠标、键盘、触控笔)的完整事件序列。当你创建一个动作链时,你实际上是在一个队列中缓存一系列动作。只有当你调用perform()方法时,这些动作才会被编译并发送给浏览器,由浏览器按照指定的顺序和时序来执行。

这种“先存储,后执行”的模式有两个关键优势:一是保证了动作的原子性,一系列操作被作为一个整体执行,中间不会被其他脚本或浏览器事件干扰;二是允许构建复杂的组合动作,比如在移动鼠标的同时按下某个键。

一个基本的动作链代码结构如下:

from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By import time driver = webdriver.Chrome() driver.get("https://example.com") # 定位到需要操作的元素 element = driver.find_element(By.ID, "someElement") # 1. 创建ActionChains对象,将driver传入 actions = ActionChains(driver) # 2. 将多个动作链接起来(此时动作并未执行) actions.move_to_element(element).click_and_hold().move_by_offset(100, 50).release() # 3. 执行所有缓存的动作 actions.perform()

注意ActionChains的方法大多返回ActionChains对象自身,这支持了链式调用。但请记住,在调用perform()之前,什么都不会发生。一个常见的错误是写了链式调用,却忘了最后执行perform()

3.2 关键鼠标动作方法实战

让我们拆解几个最常用的鼠标动作,并通过实例看看它们如何解决实际问题。

click_and_hold(on_element=None)/release(on_element=None):这是实现拖拽的黄金组合。click_and_hold模拟鼠标按下左键,release模拟松开。如果不指定on_element,则在当前鼠标位置执行。这对于滑动验证码(如拖动滑块)的自动化至关重要。

slider = driver.find_element(By.CLASS_NAME, "slider") track = driver.find_element(By.CLASS_NAME, "track") actions = ActionChains(driver) # 将鼠标移动到滑块上,按下,拖动一定距离,然后释放 actions.click_and_hold(slider).move_by_offset(300, 0).release().perform()

这里move_by_offset(xoffset, yoffset)是相对于当前鼠标位置(即滑块中心)的移动。你需要根据轨迹长度计算需要移动的像素值。

move_to_element(to_element):这是处理悬停菜单的必备技能。很多网站的次级菜单只在鼠标悬停在主菜单项上时才显示。

main_menu = driver.find_element(By.LINK_TEXT, "产品") sub_menu_item = driver.find_element(By.LINK_TEXT, "产品详情") # 初始状态不可见 actions = ActionChains(driver) # 将鼠标移动到“产品”上,触发悬停事件显示子菜单 actions.move_to_element(main_menu).perform() # !!!重要:悬停后,必须等待子菜单渲染出来,这里可以加一个短暂的固定等待或更好的显式等待 time.sleep(0.5) # 简单处理,生产环境建议用显式等待 # 现在可以点击变得可见的子菜单项了 sub_menu_item.click()

实操心得move_to_element之后,页面可能会触发JavaScript动态渲染子元素。直接紧接着点击子元素很可能失败,因为DOM可能还没更新完。最佳实践是:在perform()悬停动作后,使用显式等待(后面会讲)来等待目标子元素变为可交互状态,而不是用time.sleep

double_click(on_element)/context_click(on_element):分别模拟双击和右键点击。双击常用于激活可编辑状态(如表格单元格),右键则用于触发上下文菜单。

cell = driver.find_element(By.XPATH, "//td[@editable='true']") actions = ActionChains(driver) actions.double_click(cell).perform() # 双击后,该单元格可能变成一个输入框 input_box = driver.find_element(By.XPATH, "//td//input") input_box.send_keys("新数据")

drag_and_drop(source, target):这是一个便捷方法,将源元素拖放到目标元素。它内部等价于click_and_hold(source).move_to_element(target).release()。但在某些复杂页面,特别是使用了自定义拖拽库(如Sortable.js)的页面,这个简单方法可能失效。此时,你需要回退到使用click_and_holdmove_by_offset进行更精确的像素级控制。

3.3 键盘动作与复合动作组合

动作链同样可以模拟键盘操作,并与鼠标动作组合。

key_down(key, element=None)/key_up(key, element=None):模拟按下和松开某个修饰键(如Keys.CONTROL,Keys.SHIFT,Keys.ALT)。如果要输入普通字符,通常直接用元素的send_keys方法更简单。

from selenium.webdriver.common.keys import Keys text_area = driver.find_element(By.TAG_NAME, "textarea") actions = ActionChains(driver) # 模拟 Ctrl+A (全选) actions.key_down(Keys.CONTROL, text_area).send_keys("a").key_up(Keys.CONTROL).perform() # 注意:send_keys在这里是ActionChains的方法,它会被发送到当前焦点元素或指定的元素

复合动作案例:模拟画图假设我们要在一个画布上从点(100,100)画一条线到点(300,300)。

canvas = driver.find_element(By.ID, "myCanvas") actions = ActionChains(driver) # 将鼠标移动到画布起始点,按下,拖动到终点,释放 # move_to_element_with_offset 用于相对于元素左上角定位 actions.move_to_element_with_offset(canvas, 100, 100).click_and_hold() actions.move_by_offset(200, 200) # 相对移动 actions.release() actions.perform()

这个例子展示了如何将多个基础动作无缝衔接,完成一个复杂的交互任务。

常见问题与排查

  1. 动作不生效:首先检查是否调用了perform()。其次,确认目标元素是否真的支持这种交互方式(有些元素的点击事件是通过JS在父元素上监听的)。可以用driver.execute_script("arguments[0].scrollIntoView(true);", element)先滚动元素到视口。
  2. 拖拽位置不准move_by_offset是相对于当前鼠标位置的。如果拖拽起始点没对准,后续偏移就会累积误差。确保click_and_hold的位置准确。对于复杂拖拽,可以先用move_to_element_with_offset(element, xoffset, yoffset)精确定位到元素的某个特定点(如滑块的中心)再按住。
  3. 悬停后元素不出现:如前所述,悬停后必须等待。使用WebDriverWait等待子元素出现,是比sleep更可靠的选择。

4. 多窗口与iframe切换:管理复杂的页面上下文

4.1 窗口句柄的原理与获取

浏览器中每一个标签页或窗口,在WebDriver中都有一个唯一的标识符,称为窗口句柄。你可以把它理解为一个窗口的ID。driver.current_window_handle获取当前焦点窗口的句柄,driver.window_handles返回一个列表,包含浏览器会话中所有打开的窗口的句柄,其顺序不一定是窗口打开的顺序,但通常是按照创建时间排序的

当新窗口打开时,它会被添加到window_handles列表的末尾。这是一个非常重要的特性,是我们实现窗口切换逻辑的基础。

# 点击前,记录原始窗口 original_window = driver.current_window_handle print(f"原始窗口句柄: {original_window}") # 执行会打开新窗口的操作,例如点击一个 target="_blank" 的链接 link = driver.find_element(By.LINK_TEXT, "在新窗口打开") link.click() # 等待新窗口出现 WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1) # 获取所有窗口句柄 all_handles = driver.window_handles print(f"所有窗口句柄: {all_handles}") # 新窗口通常是最后一个,但更稳健的方法是排除原始窗口 new_window = [handle for handle in all_handles if handle != original_window][0]

4.2 稳健的窗口切换策略

直接从window_handles中取最后一个作为新窗口,在大多数情况下可行,但并非绝对可靠。更健壮的做法是结合等待和排除法。

策略一:显式等待新窗口出现这是防止操作过快导致切换失败的关键。我们使用WebDriverWait等待窗口数量增加。

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def new_window_is_opened(driver, original_handles_count): """自定义等待条件:判断是否有新窗口打开""" return len(driver.window_handles) > original_handles_count original_handles_count = len(driver.window_handles) # 触发开窗操作 driver.find_element(...).click() # 等待,直到窗口数量增加 WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > original_handles_count)

策略二:切换到最新窗口并验证切换到新窗口后,立即通过标题、URL或特定元素验证是否切换正确,这是一个好习惯。

# 切换到新窗口(假设是最后一个) driver.switch_to.window(driver.window_handles[-1]) # 验证:例如等待新窗口的某个特定元素出现 try: WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "newWindowUniqueElement")) ) print("成功切换到新窗口并验证通过。") except TimeoutException: print("切换到新窗口失败或验证元素未找到。") # 可以考虑切回原窗口或抛出异常

策略三:处理多窗口间的来回切换任务完成后,经常需要关闭新窗口并切回原始窗口。

# ... 在新窗口完成操作后 ... # 关闭新窗口 driver.close() # 切回原始窗口 driver.switch_to.window(original_window) # 注意:关闭窗口后,其句柄就从window_handles列表中移除了,此时再switch_to它会导致异常。

4.3 处理iframe/框架切换

iframe(内联框架)是页面中的嵌套页面,有自己的DOM文档。Selenium无法直接定位iframe内部的元素,必须先切换到对应的iframe上下文。

识别与切换iframeiframe可以通过索引、name/id属性或元素定位器来切换。

# 通过索引(从0开始) driver.switch_to.frame(0) # 通过name或id driver.switch_to.frame("iframe_name_or_id") # 通过WebElement(最灵活可靠) iframe_element = driver.find_element(By.XPATH, "//iframe[@class='my-iframe']") driver.switch_to.frame(iframe_element) # 操作iframe内部的元素 inner_button = driver.find_element(By.ID, "innerButton") inner_button.click() # 操作完成后,切回主文档 driver.switch_to.default_content() # 或者切回上一级父框架(如果有多层嵌套) driver.switch_to.parent_frame()

重要提示:一旦切换到iframe,你的find_element等操作范围就被限定在该iframe内。完成操作后,必须切换出来(default_contentparent_frame),否则后续寻找主页面元素的操作都会失败。这是一个非常高频的错误来源。

实战场景:处理登录弹窗(可能是iframe)很多网站的登录框是一个模态框,有时就是一个iframe。

# 1. 点击登录按钮,触发登录框 login_trigger = driver.find_element(By.LINK_TEXT, "登录") login_trigger.click() # 2. 等待并切换到登录iframe login_iframe = WebDriverWait(driver, 10).until( EC.frame_to_be_available_and_switch_to_it((By.ID, "loginIframe")) ) # 注意:EC.frame_to_be_available_and_switch_to_it 条件会直接切换进去,无需再调用 switch_to.frame # 3. 在iframe内操作 username = driver.find_element(By.NAME, "username") password = driver.find_element(By.NAME, "password") username.send_keys("your_username") password.send_keys("your_password") driver.find_element(By.XPATH, "//button[@type='submit']").click() # 4. 登录成功后,切回主页面 driver.switch_to.default_content() # 现在可以继续操作主页面内容了

5. 元素等待机制:构建稳定脚本的基石

5.1 三种等待方式深度对比

Selenium提供了三种等待方式,理解它们的区别和适用场景是写出稳定脚本的关键。

1. 强制等待:time.sleep(seconds)这是最原始、最不推荐在生产脚本中使用的方式。它让脚本无条件暂停固定时间,无论页面是否已就绪。

  • 缺点:效率极低。如果设置时间太短,元素还没加载完;设置太长,则白白浪费执行时间。网络或服务器性能的波动很容易导致脚本不稳定。
  • 唯一适用场景:在极少数需要固定间隔的动画演示,或者调试时临时插入查看效果。在正式的自动化逻辑中应尽量避免。

2. 隐式等待:driver.implicitly_wait(timeout)这是一个全局设置。在设置之后,对于整个WebDriver实例的生命周期内,每次调用find_elementfind_elements时,如果元素没有立即找到,WebDriver会轮询DOM(默认每0.5秒)直到找到该元素或超时。

  • 优点:设置简单,一劳永逸。
  • 缺点
    • 不灵活:对所有查找操作生效,无法针对特定条件(如元素可点击、可见)。
    • 与显式等待混用可能导致超时叠加:如果隐式等待10秒,显式等待也10秒,在最坏情况下,实际等待时间可能接近20秒。
    • 无法处理非查找操作的等待,比如等待某个条件成立(如staleness_of)。
  • 建议:通常不推荐使用,或者将其设置为一个较小的值(如2-3秒)作为基础保障,然后主要依靠显式等待。

3. 显式等待:WebDriverWait(driver, timeout).until(condition)这是最强大、最推荐的方式。它允许你为某个特定的条件设置等待,条件满足则立即继续,超时则抛出TimeoutException。它提供了高度的灵活性和精确控制。

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待最多10秒,直到ID为‘myElement’的元素出现在DOM中 element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myElement")) ) # 等待元素不仅存在,而且可见且可点击 clickable_element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "提交")) ) clickable_element.click()

5.2 常用 Expected Conditions 解析

expected_conditions模块提供了大量预定义的条件,以下是核心的几个:

  • presence_of_element_located(locator)元素存在于DOM树中。这是最基础的条件,但不代表元素一定可见或可交互。适合用于判断元素是否已加载。
  • visibility_of_element_located(locator)元素不仅存在,而且可见(即display不为nonevisibilityvisible,宽高大于0)。这是比presence更严格也更常用的条件,因为不可见的元素无法交互。
  • element_to_be_clickable(locator)元素可见且启用(enabled)。这是执行点击操作前的黄金等待条件。
  • text_to_be_present_in_element(locator, text_):等待元素的文本包含特定字符串。常用于验证操作结果。
  • staleness_of(element):等待元素不再附加于DOM。这在等待一个旧元素(例如,页面刷新或AJAX更新后会被替换的元素)消失时非常有用。
  • number_of_windows_to_be(num_windows):等待窗口数量变为特定值。这正是我们前面窗口切换中用到的逻辑基础。
  • frame_to_be_available_and_switch_to_it(locator):等待iframe可用并自动切换进去,非常方便。

5.3 自定义等待条件与复杂等待逻辑

当内置条件不满足需求时,你可以定义自己的等待条件。这是一个等待页面某个特定JavaScript变量被设置的例子:

def js_variable_is_set(driver, variable_name, expected_value): """自定义条件:等待JavaScript变量等于期望值""" def predicate(d): # 使用execute_script执行JS并获取返回值 actual_value = d.execute_script(f"return window.{variable_name};") return actual_value == expected_value return predicate # 使用自定义条件 WebDriverWait(driver, 10).until( js_variable_is_set(driver, "pageLoadComplete", True) ) print("页面JS加载标志已就绪。")

组合等待与轮询策略: 有时你需要等待一系列复杂条件。虽然WebDriverWait本身不支持逻辑与/或,但你可以通过循环或在一个until中调用复杂的函数来实现。

def complex_condition(driver): # 条件1:某个元素可见 cond1 = EC.visibility_of_element_located((By.ID, "elem1"))(driver) # 条件2:另一个元素的文本包含特定内容 cond2 = EC.text_to_be_present_in_element((By.ID, "status"), "完成")(driver) return cond1 and cond2 WebDriverWait(driver, 15).until(complex_condition)

更优雅的方式是使用lambda表达式组合多个内置条件检查:

WebDriverWait(driver, 15).until(lambda d: d.find_element(By.ID, "elem1").is_displayed() and "完成" in d.find_element(By.ID, "status").text )

5.4 等待策略的最佳实践与避坑指南

  1. 优先使用显式等待:这是构建稳定自动化套件的首要原则。为每个需要交互的元素或状态变化设置合适的显式等待。
  2. 明确等待目标:问自己:“我在等什么?” 如果是为了点击,就用element_to_be_clickable;如果只是为了获取元素内容,用presence_of_element_locatedvisibility_of_element_located
  3. 设置合理的超时时间:超时时间不是越长越好。应根据网络状况、应用响应速度和业务逻辑来设定。通常10-20秒是合理的范围。太短容易在慢环境下失败,太长则在出错时浪费大量时间。
  4. 避免隐式与显式等待混用:如果非要混用,请将隐式等待设置为一个很小的值(如0或2秒),并清楚了解其潜在的超时叠加效应。
  5. 处理超时异常WebDriverWait.until在超时会抛出TimeoutException。你应该捕获这个异常并进行适当的处理(如记录日志、截图、执行清理操作或标记测试失败),而不是让脚本崩溃。
    from selenium.common.exceptions import TimeoutException try: element = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, "slowLoadingElement")) ) element.click() except TimeoutException: print("元素加载超时,进行错误处理...") driver.save_screenshot("timeout_error.png") # 可能执行一些恢复操作,或者直接抛出失败 raise
  6. 等待的“范围”:记住,WebDriverWait是针对driver的当前上下文(可能是主页面,也可能是某个iframe)进行等待。如果你切换了iframe,那么等待操作也是在那个iframe的DOM中查找元素。

6. 综合实战:一个完整的自动化测试用例

让我们将动作链、窗口切换和元素等待组合起来,完成一个模拟真实用户操作流程的案例:在某个Web应用上,通过拖拽方式上传文件,并在新打开的预览窗口中确认。

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import time # 初始化驱动 driver = webdriver.Chrome() driver.maximize_window() wait = WebDriverWait(driver, 15) # 创建一个全局等待对象,超时15秒 try: # 步骤1: 打开应用主页 driver.get("https://your-web-app.com") print("已打开应用主页。") # 步骤2: 等待并点击‘上传’按钮,触发上传区域显示 upload_trigger = wait.until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), '上传')]")) ) upload_trigger.click() print("已点击上传按钮。") # 步骤3: 等待上传区域(一个可拖放区域)可见 drop_area = wait.until( EC.visibility_of_element_located((By.CLASS_NAME, "drop-zone")) ) # 步骤4: 使用动作链模拟文件拖拽操作 # 假设我们有一个本地文件路径的‘虚拟’元素需要拖拽,这里我们用动作链的‘拖拽到元素’方法 # 注意:Selenium无法直接与操作系统文件对话框交互。这里假设页面有一个隐藏的文件输入框,我们通过动作链将‘虚拟焦点’移过去并执行操作。 # 更常见的做法是直接使用 send_keys 设置文件路径到 input[type='file'] 元素。 # 但为了演示动作链,我们假设一个复杂场景:需要将代表文件的图标拖到 drop_area。 file_icon = driver.find_element(By.ID, "localFileIcon") # 假设页面上有一个代表本地文件的图标 actions = ActionChains(driver) # 将文件图标拖放到上传区域 actions.drag_and_drop(file_icon, drop_area).perform() print("已完成文件拖拽操作。") # 步骤5: 等待上传成功提示出现(假设会出现一个成功提示元素) success_msg = wait.until( EC.visibility_of_element_located((By.XPATH, "//div[contains(text(), '上传成功')]")) ) print("文件上传成功。") # 步骤6: 点击‘预览’按钮,预期会在新窗口打开预览页 preview_button = wait.until( EC.element_to_be_clickable((By.LINK_TEXT, "预览")) ) original_window = driver.current_window_handle # 记录当前窗口句柄 print(f"原始窗口句柄: {original_window}") preview_button.click() print("已点击预览按钮。") # 步骤7: 等待新窗口出现并切换 # 使用自定义条件等待新窗口数量增加 wait.until(lambda d: len(d.window_handles) > 1) all_windows = driver.window_handles new_window = [win for win in all_windows if win != original_window][0] driver.switch_to.window(new_window) print(f"已切换到新窗口,句柄: {new_window}") # 步骤8: 在新窗口中等待预览内容加载完成(例如,等待一个特定的图片或canvas元素) preview_image = wait.until( EC.visibility_of_element_located((By.ID, "previewImage")) ) print("预览页面加载完成。") # 步骤9: 在新窗口执行一些操作,例如点击‘确认’按钮 confirm_btn = wait.until( EC.element_to_be_clickable((By.XPATH, "//button[text()='确认使用']")) ) confirm_btn.click() print("已在预览窗口点击确认。") # 步骤10: 确认后,新窗口可能会自动关闭,或者我们需要手动关闭并切回原窗口 # 等待新窗口可能关闭,或者直接检查窗口数量 time.sleep(2) # 简单等待一下让操作生效,生产环境应用显式等待条件 if len(driver.window_handles) == 1: # 新窗口已关闭,自动回到原窗口 driver.switch_to.window(driver.window_handles[0]) print("新窗口已关闭,已回到原窗口。") else: # 新窗口未关闭,手动关闭并切换 driver.close() driver.switch_to.window(original_window) print("已手动关闭新窗口并切回原窗口。") # 步骤11: 在原窗口验证状态更新(例如,文件列表更新) updated_file_item = wait.until( EC.presence_of_element_located((By.XPATH, f"//div[contains(text(), 'your_filename.txt')]")) ) print("原窗口文件列表已更新,流程结束。") except TimeoutException as e: print(f"操作超时: {e}") # 这里可以添加截图和日志记录 driver.save_screenshot("error_screenshot.png") except Exception as e: print(f"发生未知错误: {e}") finally: # 清理资源 time.sleep(3) # 演示用,方便查看结果 driver.quit()

这个案例涵盖了从基础等待、复杂交互(拖拽)到多窗口管理的完整链条。它展示了如何将这些进阶技术有机地组合在一起,处理一个真实的、多步骤的用户流程。在实际项目中,你需要根据具体的网页结构来调整定位器和等待条件,但核心的框架和思路是相通的。记住,耐心(等待)和精准(动作与切换)是编写可靠Selenium脚本的不二法门。

http://www.jsqmd.com/news/1115660/

相关文章:

  • API接口平台按量计费vs包月:不同用量下的真实成本计算
  • 星露谷物语模组加载器SMAPI:3步解决macOS启动难题的完整指南
  • Si4732与PIC18F4680数字收音机方案设计与优化
  • PCF8591与PIC18F4525的I2C通信与混合信号处理实战
  • Panalog日志审计系统前台RCE漏洞复现与深度分析
  • 手把手搭建Quark Engine漏洞检测环境:从部署到自动化实战
  • LTC6904可编程振荡器与PIC单片机的高精度时钟方案
  • 模型更新策略里多久重新训练一次合理?
  • 3分钟搞定QQ音乐加密文件:macOS专业解密工具QMCDecode使用指南
  • 终极指南:如何使用RDP Wrapper解锁Windows多人远程桌面功能
  • STM32与LV30条码扫描引擎的硬件协同设计与优化
  • 解构工业软件下半场:国产厂商如何破局海外巨头垄断?
  • 一小时掌握Node.js核心:从环境搭建到HTTP服务器实战
  • 基于STM32单片机的 CO浓度检测 一氧化碳可燃报警器监测系统2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 进度管理、风险变更、成本管理、风险管理
  • STM32与AD74413R高精度混合信号处理方案
  • 2026免费视频去水印工具推荐:电脑手机在线无广告安全不压缩
  • STM32与PCF8591的硬件协同与信号处理实战
  • 2026年6款热门音轨分离、人声分离、乐器伴奏分离工具实测测评
  • ICM-42688-P与PIC18F57K42在运动检测与工业监测中的应用
  • 漫画和小说都在NAS里,却只能回家看?用Kavita打造随身数字书屋
  • 灵矽微LS12D105T完全P2P国产替代TSSOP封装的AD9235
  • Si4732与MSP432P401R数字广播接收系统设计与优化
  • VMware安装Win10操作流程
  • 15款专业字体库:设计师和开发者的终极字体解决方案
  • 2026年儿童口腔运营学习新排名,谁将脱颖而出?
  • Ice:终极macOS菜单栏管理解决方案 - 智能隐藏、美化与高效工作流指南
  • ICM-42688-P与STM32F745ZG在机器人控制与工业监测中的应用
  • 这个世界还是很混乱-----菲律宾副总统宣称要干掉总统
  • 原神帧率解锁终极指南:3步免费突破60帧限制实现120FPS流畅体验