Python+Selenium复用浏览器:原理、实战与避坑指南
1. 项目概述:为什么复用浏览器是UI自动化的“神兵利器”?
如果你做过UI自动化测试,尤其是用Python+Selenium这套黄金组合,那你一定经历过这样的场景:脚本启动,一个崭新的浏览器窗口弹出来,加载页面,执行登录,然后开始你的测试步骤。每次运行,哪怕只是改了一行代码,都得重新走一遍这个流程。登录要时间,加载要时间,更别提那些需要复杂前置状态(比如购物车里有商品、订单处于待支付状态)的测试用例了。一天下来,可能一半时间都在等浏览器启动和初始化。这效率,实在让人抓狂。
“复用浏览器”这个概念,就是为了解决这个痛点而生的。简单说,它让你能绕过每次脚本执行时那套繁琐的“打开浏览器-加载页面-登录”的初始化流程,直接连接到一个已经处于特定状态(比如已登录、已打开到某个复杂页面)的浏览器实例上进行后续操作。这不仅仅是节省了几十秒的启动时间,更是将测试脚本的稳定性和执行效率提升了一个维度。想象一下,调试一个位于订单支付流程第五步的断言时,你不再需要从第一步开始跑,而是直接让脚本“附身”到那个已经走到第四步的浏览器上,立即开始验证。这种“断点续测”的能力,对于复杂业务流程的调试和日常的快速回归验证,价值巨大。
市面上很多教程和文章会提到复用浏览器,但往往浅尝辄止,只给一个chrome_options.add_experimental_option(“debuggerAddress”, “127.0.0.1:9222”)的代码片段。这行代码背后的原理是什么?不同的浏览器(Chrome, Edge)具体怎么操作?如何稳定地启动一个可供远程调试的浏览器实例?连接上了之后,常见的坑有哪些,比如Cookie丢失、页面状态不一致?这些实战中必然会遇到的问题,才是真正决定这个技术能否用起来的关键。今天,我们就抛开那些泛泛而谈,深入到Python+Selenium复用浏览器的每一个技术细节和操作环节,从原理到实践,从操作到避坑,给你一份能直接抄作业的完整指南。
2. 核心原理与浏览器调试协议揭秘
要理解复用浏览器,不能只停留在Selenium的API调用上,必须向下窥探一层,看到浏览器本身提供的能力。这一切的核心,都围绕着一个关键词:远程调试协议。
2.1 远程调试协议:浏览器的“后门”
以Chromium内核的浏览器(如Chrome、Edge、新版Opera)为例,它们都内置了一个基于WebSocket的DevTools Protocol。这个协议本来是给Chrome DevTools(就是按F12打开的那个开发者工具)使用的,允许外部工具(比如Selenium WebDriver)向浏览器发送命令(如导航、点击、执行JS)并接收事件(如页面加载、网络请求)。当我们以调试模式启动浏览器时,浏览器会打开一个指定的TCP端口(默认是9222),并在这个端口上监听来自外部的连接。
Selenium WebDriver(特别是ChromeDriver)本质上就是一个实现了WebDriver Wire Protocol的客户端,而这个协议底层可以与DevTools Protocol进行通信。当我们通过debuggerAddress参数连接时,Selenium WebDriver就不再需要启动一个新的浏览器进程,而是直接通过这个“后门”连接到已有的浏览器实例,并接管其控制权。
注意:Firefox也有类似的机制(通过Marionette协议和
-marionette、-remote-debugging-port参数),但生态和稳定性略逊于Chromium系。Safari则需要启用“开发”菜单中的“允许远程自动化”选项。本文将以最主流的Chrome/Edge为例进行详解。
2.2 复用 vs 新建:流程对比与本质差异
为了更直观地理解复用浏览器带来的变化,我们对比一下两种模式的核心流程:
传统新建浏览器流程:
- 脚本启动,调用
webdriver.Chrome()。 - Selenium库找到并启动
chromedriver.exe进程。 chromedriver启动一个全新的、干净的chrome.exe进程(用户数据目录通常是临时生成的)。chromedriver通过内部通道与这个新的Chrome进程建立连接(通常不是9222端口)。- 浏览器加载空白页或指定首页,脚本开始执行(如导航、登录)。
复用现有浏览器流程:
- 你手动或通过另一个脚本,以特定命令行参数启动一个Chrome进程,并指定一个调试端口(如9222)和一个固定的用户数据目录。
- 这个Chrome进程在前台或后台运行,并打开了调试端口。此时,你可以手动在这个浏览器里进行任何操作:登录网站、跳转到复杂页面、添加插件等。
- 在你的自动化脚本中,配置
webdriver.ChromeOptions,添加debuggerAddress=‘127.0.0.1:9222’。 - 脚本调用
webdriver.Chrome(options=options)。 - Selenium启动
chromedriver,但chromedriver发现你指定了debuggerAddress,于是它不会启动新的Chrome进程,而是直接尝试通过WebSocket连接到你指定的127.0.0.1:9222。 - 连接成功,
chromedriver获得了对那个已存在Chrome实例的控制权。脚本可以立即操作当前已经打开的页面和状态。
看出本质区别了吗?关键在于用户数据目录和调试端口。固定的用户数据目录保证了浏览器会话、Cookie、本地存储的持久化;开放的调试端口提供了外部控制的通道。这就像是你先手动把车(浏览器)发动并开到了赛道上(特定页面状态),然后你的副驾(自动化脚本)才上车接手方向盘,继续剩下的比赛。
3. 手把手实操:启动一个可供连接的浏览器实例
理论讲完,我们进入实战。第一步也是最关键的一步:正确启动浏览器。这里不能直接用鼠标双击,必须通过命令行。
3.1 Chrome浏览器启动命令详解
打开你的终端(Windows CMD/PowerShell, macOS Terminal, Linux Shell),找到Chrome浏览器的安装路径。一个标准的启动命令如下:
# Windows 示例 "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\Temp\ChromeDebugSession" # macOS 示例 /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir="/tmp/chrome_debug_profile" # Linux 示例 google-chrome-stable --remote-debugging-port=9222 --user-data-dir="/tmp/chrome_debug"参数拆解与避坑指南:
--remote-debugging-port=9222:这是核心。指定调试协议监听的端口。9222是默认且常用的端口,你也可以改为其他未被占用的端口,如9223、9333。务必确保端口未被占用,否则启动会失败。--user-data-dir=”[路径]“:这是成败关键。它指定浏览器存储本次会话数据(Cookies、缓存、历史记录、扩展程序等)的目录。- 必须使用一个全新的、独立的目录。不要指向你日常使用的Chrome数据目录(如
~/.config/google-chrome),否则可能导致日常浏览器数据损坏或被锁定。 - 目录路径不要有中文或特殊空格。建议使用全英文路径。Windows用户如果路径包含空格,请确保用双引号包裹整个路径。
- 每次想开启一个全新的、干净的调试会话时,应更换目录名或清空该目录。复用同一个目录,则会沿用上一次会话的所有状态(包括登录态)。
- 必须使用一个全新的、独立的目录。不要指向你日常使用的Chrome数据目录(如
其他有用参数:
--no-first-run:跳过首次运行的向导。--no-default-browser-check:不进行默认浏览器检查。--disable-infobars:禁用“Chrome正在受到自动测试软件控制”的信息栏。--start-maximized:启动时最大化窗口。对于UI自动化,控制窗口大小很重要。
一个更健壮的启动命令组合可能是:
“C:\Program Files\Google\Chrome\Application\chrome.exe” --remote-debugging-port=9222 --user-data-dir=“C:\AutoTest\ChromeProfile_$(date +%s)” --no-first-run --no-default-browser-check --disable-infobars --start-maximized(注:$(date +%s)是Linux/macOS生成时间戳的方式,Windows下可以手动给目录名加个编号以示区别)。
执行命令后,一个新的Chrome窗口会打开。你可能会看到一个提示“正在等待调试...”的空白页,或者直接打开新标签页。这都正常。现在,你可以手动进行任何操作:登录你的测试系统、跳转到某个深层页面、安装必要的测试插件(如用于定位元素的SelectorGadget)。
3.2 验证调试端口是否成功开启
浏览器启动后,如何确认调试端口已打开?最简单的方法是访问一个特殊的本地URL。在你的另一个浏览器(比如你日常用的Firefox或另一个Chrome窗口)中,访问:
http://localhost:9222/json/list如果配置正确,你会看到一个JSON格式的响应,里面列出了所有可调试的标签页(Tab)信息,包括每个标签页的id、title、url以及用于连接的WebSocket地址(webSocketDebuggerUrl)。这个列表是你连接成功的铁证。
3.3 Edge浏览器及其他Chromium内核浏览器的启动
基于Chromium的新版Microsoft Edge,操作与Chrome几乎完全一致,只是可执行文件路径不同:
# Windows Edge 示例 “C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe” --remote-debugging-port=9222 --user-data-dir=“C:\Temp\EdgeDebugSession”其他如Brave、Opera (Chromium版)等,同理类推,找到其主程序路径即可。
4. Python+Selenium连接与控制已启动的浏览器
浏览器已经在9222端口待命,接下来就是让我们的Python脚本“附体”上去了。
4.1 核心代码实现与选项配置
首先,确保你已安装Selenium库:pip install selenium,并下载与你的浏览器版本匹配的chromedriver,放在系统PATH或脚本指定位置。
连接的核心代码如下:
from selenium import webdriver from selenium.webdriver.chrome.options import Options import time def connect_to_existing_browser(debugger_address=“127.0.0.1:9222”): “”” 连接到已运行的、开启了远程调试的Chrome/Edge浏览器实例。 “”” chrome_options = Options() # 这是最关键的一行:指定调试器地址 chrome_options.add_experimental_option(“debuggerAddress”, debugger_address) # 通常情况下,连接已有浏览器时不需要再指定chromedriver路径,系统PATH中有即可。 # 如果你有多个版本或指定路径,可以取消下面这行的注释。 # driver_path = “./drivers/chromedriver” # driver = webdriver.Chrome(executable_path=driver_path, options=chrome_options) driver = webdriver.Chrome(options=chrome_options) # 连接成功后,打印当前所有窗口的句柄和URL,确认状态 print(f“成功连接!当前窗口句柄:{driver.current_window_handle}”) print(f“当前URL:{driver.current_url}”) print(f“所有窗口句柄:{driver.window_handles}”) # 通常,连接后会聚焦在浏览器当前激活的标签页。 # 你可以通过driver.window_handles和driver.switch_to.window(handle)来切换标签页。 return driver if __name__ == “__main__”: # 假设你的浏览器正在localhost的9222端口监听 driver = connect_to_existing_browser(“127.0.0.1:9222”) # 现在,driver对象已经完全控制了那个手动打开的浏览器。 # 你可以像操作普通driver一样操作它。 try: # 示例:获取当前页面标题 print(f“页面标题:{driver.title}”) # 示例:如果当前页面是百度,在搜索框输入内容 # 注意:这里的前提是你手动打开的浏览器当前标签页正好是百度。 # search_box = driver.find_element(By.ID, “kw”) # search_box.send_keys(“复用浏览器测试”) # search_box.submit() # time.sleep(2) # 更多你的测试逻辑... finally: # 重要决策点:是否关闭浏览器? # driver.quit() # 这会关闭整个浏览器进程!慎用! # driver.close() # 这只关闭当前标签页,如果只剩一个标签页,则会关闭浏览器。 print(“测试操作完成。注意:调用driver.quit()会终止浏览器进程。”)4.2 连接后的状态管理与注意事项
成功连接后,有几个关键点需要立刻理清:
当前焦点标签页:
driver默认控制的是浏览器中当前激活(active)的标签页。如果你手动打开了多个标签页,需要先用driver.window_handles获取列表,再driver.switch_to.window(handle)进行切换。浏览器进程的生命周期:通过
debuggerAddress连接的driver,调用driver.quit()时,会关闭整个被连接的浏览器进程!这与常规模式下driver.quit()只关闭它自己启动的浏览器不同。因此,在调试脚本时,如果你还希望保留那个手动打开的浏览器窗口以供下次使用,请避免在脚本末尾调用driver.quit()。通常,只关闭不用的标签页(driver.close())即可。Cookie与本地存储:由于连接的是同一个浏览器实例,且使用了固定的
user-data-dir,所以你在该浏览器里手动登录产生的Cookie、LocalStorage、SessionStorage,对连接的driver是完全可见且可用的。这是复用浏览器实现“已登录状态”测试的基石。浏览器扩展(Extensions):在手动启动浏览器时安装的扩展(比如广告拦截器、前端调试工具),在连接后同样存在。这有时是好事(可以用你熟悉的插件辅助),有时也可能是干扰(某些插件可能影响页面元素定位或行为)。请注意这一点。
5. 实战进阶:构建稳定的复用浏览器测试框架
掌握了单次连接,我们要把它工程化,融入到日常的自动化测试框架中。目标是:一键启动/连接,状态持久化,多测试用例共享会话。
5.1 封装浏览器启动与连接工具类
我们可以创建一个工具类,来管理调试浏览器的生命周期。
# browser_reuse_tool.py import subprocess import os import time import psutil # 需要安装:pip install psutil from selenium import webdriver from selenium.webdriver.chrome.options import Options class ReusableBrowser: def __init__(self, browser_path=None, user_data_dir=None, port=9222): “”” 初始化可复用浏览器管理器。 :param browser_path: 浏览器可执行文件完整路径。如果为None,尝试自动查找。 :param user_data_dir: 用户数据目录。如果为None,使用临时目录。 :param port: 远程调试端口。 “”” self.port = port self.browser_path = browser_path or self._find_chrome_path() self.user_data_dir = user_data_dir or os.path.join(os.getenv(“TEMP”, “/tmp”), f“chrome_debug_{int(time.time())}”) self.browser_process = None self.driver = None def _find_chrome_path(self): “”“尝试自动查找系统上的Chrome路径。”“” # 这里简化处理,实际项目中可能需要更健壮的查找逻辑 possible_paths = [ “C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe”, “C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe”, “/Applications/Google Chrome.app/Contents/MacOS/Google Chrome”, “/usr/bin/google-chrome-stable”, “/usr/bin/chromium-browser” ] for path in possible_paths: if os.path.exists(path): return path raise FileNotFoundError(“未找到Chrome浏览器路径,请手动指定browser_path参数。”) def start_browser(self): “”“以调试模式启动浏览器进程。”“” # 确保用户数据目录存在 os.makedirs(self.user_data_dir, exist_ok=True) # 构建启动命令 cmd = [ self.browser_path, f“--remote-debugging-port={self.port}”, f“--user-data-dir={self.user_data_dir}”, “--no-first-run”, “--no-default-browser-check”, “--disable-infobars”, “--start-maximized” ] print(f“启动浏览器命令:{‘ ‘.join(cmd)}”) # subprocess.Popen 启动,不阻塞当前脚本 self.browser_process = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # 等待浏览器启动并打开调试端口 time.sleep(3) # 简单等待,生产环境建议用循环检测端口是否就绪 print(f“浏览器已启动在端口 {self.port},用户数据目录:{self.user_data_dir}”) def connect_driver(self): “”“连接Selenium WebDriver到已启动的浏览器。”“” if not self._is_port_in_use(self.port): raise ConnectionError(f“端口 {self.port} 未被占用,请先启动浏览器。”) chrome_options = Options() chrome_options.add_experimental_option(“debuggerAddress”, f“127.0.0.1:{self.port}”) try: self.driver = webdriver.Chrome(options=chrome_options) print(f“WebDriver 连接成功。当前URL: {self.driver.current_url}”) return self.driver except Exception as e: raise ConnectionError(f“连接WebDriver失败:{e}”) def _is_port_in_use(self, port): “”“检查指定端口是否被占用。”“” import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex((‘127.0.0.1’, port)) == 0 def cleanup(self, kill_browser=True): “”“清理资源。”“” if self.driver: try: self.driver.quit() # 注意:这会关闭浏览器进程! except: pass self.driver = None if kill_browser and self.browser_process: # 如果driver.quit已经关闭了浏览器,这里再kill可能报错,但无害 try: self.browser_process.terminate() self.browser_process.wait(timeout=5) except (psutil.NoSuchProcess, subprocess.TimeoutExpired): pass self.browser_process = None print(“资源清理完成。”) # 使用示例 if __name__ == “__main__”: rb = ReusableBrowser(port=9333) # 使用9333端口,避免冲突 try: rb.start_browser() driver = rb.connect_driver() # 此时可以手动在浏览器中操作,比如登录 input(“请手动在打开的浏览器中完成登录等操作,然后按回车键继续自动化测试...“) # 自动化脚本继续执行 driver.get(“https://www.example.com/my-test-page”) # ... 你的测试逻辑 finally: # 测试结束,清理。如果不希望关闭浏览器,设置kill_browser=False rb.cleanup(kill_browser=True)5.2 集成到Pytest/Unittest测试框架
在自动化测试框架中,我们通常希望在每个测试类或模块开始时,建立浏览器连接,所有测试用例共享这个会话,并在最后统一清理。
# conftest.py (Pytest 示例) import pytest from browser_reuse_tool import ReusableBrowser @pytest.fixture(scope=“session”) # 会话级别,所有测试用例共享同一个浏览器实例 def shared_browser_session(request): “”“启动并连接一个可复用的浏览器,贯穿整个测试会话。”“” rb = ReusableBrowser(port=9222, user_data_dir=“./test_chrome_profile”) rb.start_browser() # 这里可以加入初始化的公共操作,比如访问登录页 driver = rb.connect_driver() # driver.get(“https://test.com/login”) # ... 执行公共登录逻辑 yield driver # 将driver对象提供给测试用例 # 测试会话结束后清理 rb.cleanup(kill_browser=True) # test_order.py import pytest from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestOrderWithReusedBrowser: “”“假设在shared_browser_session中,我们已经处于登录状态并打开了订单页面。”“” def test_view_order_list(self, shared_browser_session): driver = shared_browser_session # 因为浏览器状态是复用的,我们可能已经在订单列表页 # 直接定位元素进行断言 wait = WebDriverWait(driver, 10) order_table = wait.until(EC.presence_of_element_located((By.ID, “order-list”))) assert order_table.is_displayed() # 更多订单列表的断言... def test_create_new_order(self, shared_browser_session): driver = shared_browser_session # 点击“新建订单”按钮 new_order_btn = driver.find_element(By.ID, “create-order-btn”) new_order_btn.click() # 在新建订单页面填写表单并提交 # ... 表单操作逻辑 # 断言订单创建成功 success_msg = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, “alert-success”)) ) assert “创建成功” in success_msg.text这种模式下,setUp和tearDown只执行一次,所有测试用例都在同一个已登录的浏览器会话中快速执行,避免了重复登录,极大提升了测试套件的执行速度。
6. 避坑指南与常见问题排查
复用浏览器很强大,但坑也不少。下面是我在多年实践中总结的典型问题及解决方案。
6.1 连接失败:Address already in use 或 Connection refused
- 问题现象:启动浏览器时提示端口被占用,或者Python脚本连接时抛出
ConnectionRefusedError。 - 排查与解决:
- 端口冲突:确保你指定的端口(如9222)没有被其他程序占用。可以用命令检查(Linux/macOS:
lsof -i:9222, Windows:netstat -ano | findstr :9222)。换个端口试试。 - 浏览器未以调试模式启动:确认启动命令中包含了
--remote-debugging-port参数,并且没有拼写错误。 - 浏览器启动失败:检查
user-data-dir路径是否有写权限,路径名是否合法。尝试用一个简单的、绝对存在的路径(如C:\Temp\test1)。 - 防火墙或安全软件拦截:极少数情况下,本地回环地址(127.0.0.1)的特定端口可能被拦截。暂时关闭防火墙试试。
- 端口冲突:确保你指定的端口(如9222)没有被其他程序占用。可以用命令检查(Linux/macOS:
6.2 连接成功但无法操作页面/页面状态不对
- 问题现象:
driver连接上了,但current_url不是你手动打开的页面,或者操作元素时找不到。 - 排查与解决:
- 焦点标签页错误:连接后,Selenium控制的是浏览器中当前激活的标签页。如果你手动打开了多个标签页,需要先切换到正确的标签页。使用
driver.window_handles和driver.switch_to.window来切换。 - 页面尚未加载完成:虽然你手动打开了页面,但连接瞬间页面可能还在加载。在关键操作前增加显式等待(WebDriverWait)。
- 用户数据目录污染:如果你复用了旧的、有问题的
user-data-dir,可能会导致浏览器状态异常。尝试用一个全新的、空的目录。
- 焦点标签页错误:连接后,Selenium控制的是浏览器中当前激活的标签页。如果你手动打开了多个标签页,需要先切换到正确的标签页。使用
6.3 脚本执行后浏览器被意外关闭
- 问题现象:测试脚本运行完毕,手动打开的浏览器窗口也一起关闭了。
- 原因与解决:这是最常见也最需要注意的一点。通过
debuggerAddress连接的driver,调用driver.quit()会关闭整个浏览器进程。- 解决方案:在调试和需要保留浏览器状态的场景下,不要在脚本中调用
driver.quit()。如果只想关闭当前标签页,用driver.close()。如果最后一个标签页被关闭,浏览器进程可能仍会退出,这与浏览器本身的行为有关。更稳妥的做法是,将清理浏览器的逻辑独立出来,在确定不需要时才执行。
- 解决方案:在调试和需要保留浏览器状态的场景下,不要在脚本中调用
6.4 多脚本并发执行时的冲突
- 问题现象:多个测试脚本同时尝试连接同一个调试端口,导致混乱或失败。
- 解决方案:
- 隔离端口:为每个并发的测试任务分配不同的调试端口(如9222, 9223, 9224...)和不同的
user-data-dir。 - 使用独立的浏览器实例:每个并行任务启动自己独立的调试浏览器实例。这需要一定的进程管理能力,但能实现真正的隔离。
- 使用Selenium Grid或Docker:对于复杂的并发UI测试,更专业的做法是使用Selenium Grid来管理多个浏览器节点,或者为每个测试用例启动一个独立的Docker容器(内含浏览器),这超出了本文“复用浏览器”的范畴,但却是企业级实践的方向。
- 隔离端口:为每个并发的测试任务分配不同的调试端口(如9222, 9223, 9224...)和不同的
6.5 浏览器版本与ChromeDriver版本不匹配
- 问题现象:连接成功,但执行某些操作时报错,提示
unknown command或invalid session id。 - 排查与解决:即使复用浏览器,
chromedriver的版本也必须与浏览器主版本匹配。确保你使用的chromedriver版本与手动打开的Chrome/Edge版本兼容。可以去官方站点下载对应版本的驱动。
7. 性能优化与最佳实践建议
将复用浏览器技术用到极致,还需要一些优化技巧。
会话持久化与复用:将登录等耗时操作做成“预热脚本”。每天上班第一件事,运行一个脚本启动调试浏览器并完成登录,然后这个浏览器实例可以挂在那里一整天。后续的所有调试和测试脚本都连接它,省去无数次登录时间。
关键状态检查与恢复:在连接浏览器后,第一个操作不应该是直接开始测试,而应该是一个“状态检查”。例如,检查当前URL是否在预期范围内,检查页面是否存在某个代表已登录的元素(如用户头像)。如果状态不对,则自动执行恢复操作(如导航到登录页并重新登录)。这能增加脚本的健壮性。
结合Page Object Model (POM):复用浏览器与POM设计模式是绝配。你的Page Object类可以设计得更加“状态感知”。例如,
OrderPage类的构造函数可以检查当前是否已经在订单页面,如果不在,则先执行跳转。用于调试,而非CI/CD:需要明确,这种手动启动浏览器并复用会话的模式,主要价值在于本地开发、调试和快速回归验证。在持续集成(CI/CD)流水线中,通常需要的是完全干净、可重复、隔离的环境,因此可能不适合直接使用这种模式。在CI中,更常见的做法是使用无头(Headless)模式或配合Selenium Grid/Docker来管理浏览器实例。
安全提醒:以调试模式运行的浏览器,其所有标签页和功能都可能通过本地网络端口被控制。切勿在生产环境或个人敏感数据环境中长期开启调试模式的浏览器,也不要将调试端口暴露在公网。这相当于给你的浏览器开了一个没有密码的后门。
复用浏览器不是UI自动化的银弹,但它是一把极其锋利的“手术刀”,专门用于解决特定场景下的效率痛点。当你需要反复测试一个深埋在产品流程中的功能时,当你需要调试一个依赖于复杂前置状态的交互时,这套方法能帮你把等待和准备的时间从几分钟压缩到几秒钟。理解其原理,掌握其操作,避开其中的坑,你就能在UI自动化的效率之路上,迈出坚实的一大步。