浏览器操作演示 · 打开关闭

打开与关闭页面

首页 浏览器操作导航

本页说明 —— 打开 / 关闭页面

演示浏览器生命周期管理的 三个核心动作:启动 → 打开页面 → 关闭/退出。看似简单,但多标签、异常中断等场景下有诸多细节。

三个 API 的区别:

⚠️ 重要提醒:脚本里忘了写 driver.quit(),任务管理器会残留 chromedriver 进程,长期积累会吃光内存。建议用 try/finally 保证无论成功失败都执行 quit()。

页面访问

打开 / 关闭页面

get / close / quit / 页面生命周期
页面的打开与关闭
浏览器自动化的第一步就是"打开目标页面",最后一步是"关闭浏览器"。Selenium 提供了 get、close、quit 三个 API 覆盖完整生命周期。
核心区别: get() 打开页面(导航) close() 关当前页签 quit() 退整个浏览器
⚠️ 常见坑: close() 不关浏览器进程,多标签场景下别用它收尾 | quit() 测试结束务必调用,否则残留进程会吃内存
对应 API:
driver.get("https://xxx") 打开指定网址(https://autotest-505.pages.dev/,点击网址在同一浏览器窗口打开新标签页)
driver.close() 关闭当前页签
driver.quit() 退出整个浏览器(所有页签)
新标签页
about:blank
AutoTest - 首页
Home Setup Locator Action

欢迎来到自动化测试练习平台

这是一个模拟的首页内容,用于演示 driver.get() 打开页面的效果。实际项目中这里会显示完整的首页。

🎯 元素定位
9 种定位方式从入门到精通
🖱️ 元素操作
点击/输入/等待/拖拽全场景
🧪 测试框架
unittest / pytest 实战演练
当前页签已被 driver.close() 关闭
Python Selenium · 打开与关闭浏览器完整示例
# ① 启动 Chrome 浏览器
from selenium import webdriver
from selenium.webdriver.common.by import By
import psutil, time   # 进程对比用,需先 pip install psutil

# 采集 chrome / chromedriver 进程 PID 集合的辅助函数
def procs():
    return {p.pid for p in psutil.process_iter(["name"])
            if p.info["name"] in ("chrome.exe", "chromedriver.exe")}

_pids = procs()   # 启动前基线,排除你自己开着的 Chrome
driver = webdriver.Chrome()

# ② 打开演示页面(此时只有这一个标签页)
driver.get("https://autotest-505.pages.dev/browser_op_tech_sections/open_close_tech.html")
print("启动后新增进程:", procs() - _pids)   # chromedriver.exe + 若干 chrome.exe

# ③ 点击“对应 API”区域中的 https://autotest-505.pages.dev/ 链接
#    该链接 target="_blank",点击后会在【同一浏览器窗口】里新开一个标签页
#    注意:点击后 driver 的焦点仍停留在原标签页,这里不做标签页切换
driver.find_element(By.CSS_SELECTOR,
                    'a[href="https://autotest-505.pages.dev/"]').click()
time.sleep(1)   # 等新标签页进程稳定
print("点击后新增进程:", procs() - _pids)   # 新标签页触发 Chrome 多进程架构创建渲染进程(也可能复用)

# ④ close():只关闭 driver 当前所在标签页,另一个不受影响;用 3 个信号对比(和 quit 后三行完全镜像)
driver.close()
print("close 后残留进程:", procs() - _pids)                     # 非空集合:chrome/chromedriver 都还活着
print("chromedriver 进程状态:", driver.service.process.poll())  # None:进程仍在运行(未退出)
print("驱动还能连接吗:", driver.service.is_connectable())     # True:会话仍存活

# ⑤ quit():关闭所有剩余标签页并退出浏览器进程(无需先切换标签);用 3 个确定性信号佐证
driver.quit()
time.sleep(1)   # 等系统回收进程
print("quit 后残留进程:", procs() - _pids)                    # set():chrome/chromedriver 全部退出
print("chromedriver 退出码:", driver.service.process.poll())  # 0:驱动进程已正常结束
print("驱动还能连接吗:", driver.service.is_connectable())     # False:driver 已释放
# 注意:Selenium 4 的 quit() 不会把 driver.session_id 置空,不要用它判断会话是否结束
操作效果展示区域
点击上方按钮,这里会显示 Selenium API 执行结果和关键数据

关于我们

Python 自动化测试学习平台是一个面向软件测试工程师、开发人员及在校学生的交互式在线学习平台,专注于 Selenium WebDriver 与 Python unittest 自动化测试技能的教学与实践。

我们以"理论 + 实战 + 可交互演示"为核心特色,将八种常规元素定位和Javascript定位、元素操作、自动化测试工具、单元测试、测试模型等知识体系拆分为模块化课程,每个模块都配有可点击、可输入、可运行的模拟练习页面。

平台致力于降低自动化测试的入门门槛,让学习者从"看懂代码"到"动手实操",逐步掌握企业级自动化测试框架的设计与落地能力。

联系方式

📧邮箱:lq@selenium-auto-test.com

📞电话:0755-12345678

📍地址:深圳

💬微信公众号:Selenium自动化测试

🐙GitHub:github.com/selenium-auto-test

免责声明

1. 本平台所有示例代码、演示页面及教程内容均仅供学习交流使用,不构成任何商业推广或技术服务承诺。

2. 平台中的 Selenium API 描述、Python 语法说明均以官方文档为准,如有版本差异请以对应官方文档为准。

3. 学习者在实际项目中应用平台所学内容时,应结合自身项目环境进行充分测试,本平台不对因直接使用示例代码而产生的任何直接或间接损失承担责任。

4. 本平台所引用的第三方库(Selenium、ChromeDriver、unittest 等)版权归其各自作者及维护者所有。

5. 如涉及版权问题或内容建议,请及时通过联系方式与我们沟通,我们将第一时间处理。

CI/CD:自动化测试的真实运行环境
CI/CD 是什么?

● CI = Continuous Integration(持续集成)
代码推送到 Git 仓库后,自动拉代码 → 装依赖 → 跑测试,每次 push 都帮你检查有没有问题。

● CD = Continuous Delivery(持续交付)
CI 通过后自动打包 → 发到服务器 → 重启,不用手动部署。
(另一种 Continuous Deployment 是全自动部署到生产,不用人审批)

CI/CD 完整流水线

你写了一行代码 → git push origin main
↓
GitHub(或 GitLab / Jenkins / Gitee)收到推送
↓
自动触发一条 Pipeline(流水线)
① Checkout(拉代码)
把你刚推的代码拉到一台全新的云服务器上
↓
② Install(装依赖)
pip install -r requirements.txt
装 Chrome、装 ChromeDriver
↓
③ Build(构建)
打包成可部署的产物(.tar.gz / Docker 镜像 / ...)
↓
④ Test(跑测试)
pytest unit_tests/  # 单元测试
pytest selenium_tests/ # ⬅️ Selenium 在这里上场!
代码覆盖率检查 · 代码风格检查
↓
全部通过?
╱    ╲
是    否
↓    ↓
⑤ Deploy
自动发到服务器
生产环境立刻更新
❌ 失败
发邮件/钉钉通知你
修复 → 重新 push

Selenium 在 CI/CD 里扮演什么角色?
Selenium 测试脚本是流水线 第 ④ 阶段(Test) 的核心。CI 服务器会:
1. 在全新的云服务器上装 Python + Chrome + ChromeDriver
2. 以 headless(无头)模式启动 Chrome(没有 GUI,更快但渲染行为和本地略有差异)
3. 运行你的 pytest + Selenium 脚本,模拟真实用户操作(登录、搜索、下单...)
4. 全部通过才允许部署到生产;任何一步失败 → 发邮件通知 → 生产不会被污染

核心好处:每次代码合进来都有 Selenium 帮你模拟真实用户过一遍关键路径,而不是等到用户反馈"登录不了了"。

为什么 CI/CD 上 sleep 特别容易崩?
CI 环境每次都是全新的云服务器,和本地有几个关键差异:
因素 本地 CI/CD 云服务器
网络专线稳定数据中心共享,偶发抖动
CPU独占多租户,可能被抢占
浏览器缓存有没有(每次全新启动)
页面加载速度有缓存,秒出全量下载,慢 2~5 倍
并发任务只跑你这一份可能几十上百个同时抢资源

同样的 sleep(3):
本地:按钮 2s 出来 → sleep 3s → 白等 1s 但能过 ✅
CI 上:按钮 6s 出来 → sleep 3s 已经过了 → find_element 找不着 → 💥

这就是为什么说 CI/CD 上尤其不稳定 —— CI 是一台你控制不了但天天要给你打分的云电脑,每次环境都不一样。
而 WebDriverWait 是"等条件满足就行,最多等 10s 上限" —— 对 CI 环境的波动天然免疫。
为什么推荐 ≤1s
📌 隐式等待超时值的语义(W3C 规范定义)
隐式等待的"兜底窗口"即 implicitly_wait(N) 所设置的 N 值本身,此结论直接来自 W3C WebDriver 规范:
"A session has an associated session implicit wait timeout that specifies a time to wait for the implicit element location strategy"
→ 驱动在一次 findElement / findElements 调用中,最多愿意等待的总时间即为该值,不得超时。
implicit=0.5s → 驱动最多等待 500ms 后必须放弃
implicit=1.0s → 驱动最多等待 1000ms 后必须放弃
implicit=0 → 驱动立即执行一次查询,不进行重试(默认值)

以上定义为 W3C 规范层面的硬约束,与驱动实现无关,是选择 implicit 值、计算叠加成本、规划白等上限的直接依据。
🔍 驱动内部轮询机制——规范未作规定
W3C 规范对隐式等待仅定义了超时值的语义(见上文),对驱动内部的轮询实现细节完全未做约束。具体而言,规范未规定: 以 ChromeDriver 为例,可通过让元素延迟插入 DOM、记录 find_element 实际耗时的方法进行观测:在常规负载下元素出现后约 50~70ms 内即可被查到;若同一驱动连续处理多次请求,该值可能飘至 200ms 以上。换驱动版本或换机器,结果也可能不同。
🔧 实测代码(可自行跑一遍验证你的驱动) 原理:让元素在指定延迟后才插入 DOM,调用 find_element 记录实际耗时。耗时减去延迟得到"元素出现后驱动最晚多久查到"。
延迟点密集覆盖 10~1500ms,跑一轮后取差值区间的上界,即为该驱动在当前负载下的轮询间隔量级。
同一驱动连续处理多次查找请求,若差值区间明显漂移,说明驱动的轮询节奏已不稳定。
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

def test_polling(d, delay):
    d.implicitly_wait(2)
    d.get('about:blank')
    d.execute_script(f"setTimeout(()=>{{document.body.innerHTML='
t
'}},{delay})") t0 = time.perf_counter() try: cost = (time.perf_counter() - t0) * 1000 print(f"delay={delay:4d} 耗时={cost:6.0f} 差值={cost-delay:+5.0f}") except: print(f"delay={delay} 超时") driver = webdriver.Chrome() for delay in range(10, 1550, 50): # 50ms 步进,覆盖 10~1500ms test_polling(driver, delay) driver.quit()

⚖️ 那 ≤1s 怎么选出来的?—— 经验 + 权衡
经验观察
竞态(race condition,脚本发起 find_element 的时刻与元素实际出现于 DOM 的时刻之间的竞态)通常差 200~500ms: 注:以上是前端性能的普遍经验数字,不是数学公式,极端场景可能超出范围。

权衡一:为什么选 1s 而不是 0.5s?
500~1000ms 是 0.5s 和 1.0s 的兜底能力分水岭:
低于 500ms 两者都能兜;高于 1000ms 两者都兜不住(该用显式等待);
只有在 500~1000ms 之间,1.0s 比 0.5s 多出了实际价值: 代价对比(和显式等待 explicit=15s 混用时):
0.5s 叠加成本 = 0.5 × 30 = 15 秒 
1.0s 叠加成本 = 1.0 × 30 = 30 秒(多 15 秒)

→ 多花 15 秒叠加成本,换 500~1000ms 档竞态(race condition)的兜底覆盖。这是一个合理的折中,不是数学必然。
权衡二:为什么不设 2s?
2s 的兜底窗口确实更大,但代价全部翻倍,而兜底提升边际效益递减:

① 叠加变长成本:和显式等待混用时的最坏耗时公式:
最坏 = implicit × (explicit超时 / pollFreq)
注:这里的 pollFreq 是 显式等待 WebDriverWait 的 poll_frequency(Selenium Python 客户端默认 500ms,硬编码在 wait.py 里),不是隐式等待的轮询间隔。这个值是确定的。

假设 explicit=15s、pollFreq=0.5s:
implicit=1s → 1 × 30 = 30 秒 ✅ 可接受
implicit=2s → 2 × 30 = 60 秒 ⚠️ 翻倍(多 30 秒,兜底窗口只多 1000ms)
implicit=5s → 5 × 30 = 150 秒 💀 爆炸

② "确认元素不存在"的白等:你写了 10 个 try: find_element ... except NoSuchElementException:
implicit=1s → 白等 10 秒
implicit=2s → 白等 20 秒(翻倍)
implicit=5s → 白等 50 秒(爆炸)

竞态(race condition)通常差 200~500ms,1s 的兜底窗口完全兜得住绝大多数。
从 1s 升到 2s,兜底窗口只多了 1000ms,但叠加成本和白等全部翻倍——边际效益递减太明显。
✅ 最终结论
值 兜底窗口
(规范定义)
叠加成本
explicit=15s
白等
10次不存在
依据类型 推荐
不设(=0)0ms0 秒0 秒规范最严谨
1s1000ms30 秒10 秒经验+权衡默认推荐
0.5s500ms15 秒5 秒经验+权衡确认页面 <500ms
2s2000ms60 秒20 秒代价太大谨慎
≥5s≥5000ms150 秒+50 秒+爆炸❌ 绝对避免