Selenium自动化测试入门:从环境搭建到实战应用 1. 项目概述为什么我们需要Selenium如果你是一名测试工程师、开发人员或者任何需要和网页打交道的从业者听到“Selenium”这个词大概率不会陌生。它几乎是Web自动化测试的代名词但它的能力远不止于此。简单来说Selenium是一个让你能用代码“遥控”浏览器的工具集。想象一下你需要每天登录一个后台系统导出几百条数据报表或者需要验证一个电商网站的下单流程在每次更新后是否正常。手动操作不仅枯燥、耗时而且容易出错。Selenium就是来解决这个问题的它允许你编写脚本模拟真人用户的所有操作——打开网页、点击按钮、输入文字、提交表单、验证结果——并且可以7x24小时不知疲倦地、精确地执行。我接触Selenium已经超过十年从最早的Selenium RCRemote Control到现在的Selenium WebDriver见证了它从一个简单的浏览器操控工具演变为一个遵循W3C标准、支持多语言、生态庞大的自动化框架。它的核心价值在于“标准化”和“可编程”。通过一套统一的WebDriver API你可以用Python、Java、C#、JavaScript等语言编写脚本这些脚本理论上可以在Chrome、Firefox、Edge、Safari等主流浏览器上运行无需为每个浏览器重写逻辑。这为跨浏览器兼容性测试、回归测试、数据抓取在合规前提下、甚至是一些重复性的网页操作自动化提供了坚实的技术基础。对于初学者可能会被“自动化测试框架”这个名头吓到觉得门槛很高。其实不然它的入门曲线相当平缓。你只需要掌握一门编程语言的基础语法理解一些HTML和CSS的基本概念用于定位页面元素就可以开始你的自动化之旅。无论是想提升测试效率的开发人员还是希望将重复工作自动化的业务人员Selenium都是一个极具性价比的起点。接下来我将从最实际的安装和基础使用入手带你一步步搭建环境并深入讲解那些官方文档可能不会细说的“坑”和技巧。2. 环境准备与Selenium安装全攻略安装Selenium听起来简单但却是新手遇到的第一个“拦路虎”问题往往出在环境配置的细节上。一个完整的Selenium工作环境需要三个部分编程语言环境、Selenium语言绑定库、以及浏览器驱动程序。我们以目前最流行的组合Python Chrome Selenium为例详细拆解每一步。2.1 Python环境与Selenium库安装首先确保你的电脑上安装了Python。建议使用Python 3.7及以上版本。你可以打开命令行Windows的CMD或PowerShellMac/Linux的Terminal输入python --version或python3 --version来检查。安装Selenium库极其简单使用Python的包管理工具pip即可。在命令行中执行以下命令pip install selenium这条命令会从Python官方的软件仓库下载并安装最新稳定版的Selenium库。这里有一个非常重要的实操心得尽量避免使用pip install selenium某个特定旧版本除非你有明确的兼容性需求。因为Selenium的版本需要与浏览器驱动版本大致匹配使用最新版库能减少很多奇怪的问题。安装完成后可以在Python交互环境中输入import selenium来验证是否成功。注意如果你在公司内网环境可能需要配置pip的镜像源如清华源、阿里云源来加速下载。命令类似pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple。2.2 浏览器驱动的“暗坑”与Selenium Manager这是Selenium安装中最关键也最容易出错的一步。Selenium脚本本身并不能直接控制浏览器它需要通过一个名为“WebDriver”的中间件来发送指令。这个WebDriver是一个独立的可执行文件需要与你电脑上安装的浏览器版本严格匹配。以前你需要手动做两件事查看自己Chrome浏览器的版本在浏览器地址栏输入chrome://settings/help。去一个叫ChromeDriver的官网通常是storage.googleapis.com开头的地址找到对应版本的驱动下载解压后放在某个目录并将该目录添加到系统的PATH环境变量中。这个过程繁琐且容易出错特别是Chrome浏览器经常自动更新导致驱动版本不匹配从而报出令人头疼的SessionNotCreatedException或This version of ChromeDriver only supports Chrome version XXX错误。幸运的是Selenium 4.6版本之后引入了一个“救星”——Selenium Manager。这是一个用Rust编写的后台工具当你初始化一个WebDriver实例时例如webdriver.Chrome()如果Selenium没有检测到合适的驱动Selenium Manager会自动尝试为你下载、匹配和配置正确的浏览器驱动。对于新手来说这极大地简化了入门流程。理论上你只需要安装好Selenium库和浏览器代码就能跑了。但是这里有一个必须了解的注意事项Selenium Manager的自动下载功能在某些网络环境下例如公司内网或网络受限环境可能会失败。它会回退到查找系统PATH中已配置的驱动如果找不到就会报错。因此了解手动配置驱动的方法仍然是必备技能。手动配置浏览器驱动的稳妥步骤确定浏览器版本打开Chrome点击右上角三个点 - 帮助 - 关于Google Chrome记下版本号例如128.0.6613.138。下载对应驱动访问ChromeDriver的官方下载站或可靠的镜像站。下载的驱动主版本号必须与你的Chrome主版本号一致例如Chrome是128版就找ChromeDriver 128.x.x.x。放置驱动将下载的chromedriver.exeWindows或chromedriverMac/Linux文件放在一个固定的、无空格和中文的目录下比如C:\WebDriver\或/usr/local/bin/。指定驱动路径推荐方法在你的代码中通过service参数显式指定驱动路径这是最清晰、最可控的方式。from selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定你的chromedriver.exe的绝对路径 service Service(executable_pathr‘C:\WebDriver\chromedriver.exe’) driver webdriver.Chrome(serviceservice)这样做的好处是你的项目不依赖全局环境变量便于团队协作和项目部署。你可以把驱动文件放在项目目录里用相对路径引用。2.3 验证安装编写并运行你的第一个脚本让我们创建一个最简单的脚本来验证整个环境是否工作正常。创建一个新的Python文件比如first_test.py输入以下代码from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By import time # 1. 创建Service对象指定驱动路径如果使用Selenium Manager自动管理可以省略Service参数 # service Service(‘你的驱动路径‘) # driver webdriver.Chrome(serviceservice) driver webdriver.Chrome() # 尝试使用Selenium Manager自动管理 # 2. 打开网页 driver.get(“https://www.baidu.com“) print(f“当前页面标题是{driver.title}“) # 3. 进行一个简单操作在搜索框输入“Selenium”并搜索 # 首先定位搜索框元素。通过F12开发者工具查看百度搜索框的id是‘kw’ search_box driver.find_element(By.ID, “kw”) search_box.send_keys(“Selenium安装教程”) # 模拟键盘输入 search_box.submit() # 提交表单 # 4. 等待一下查看结果 time.sleep(3) # 这是一个简单的固定等待实际项目中应用更智能的等待后面会讲 print(f“搜索后页面标题是{driver.title}“) # 5. 关闭浏览器 driver.quit()运行这个脚本(python first_test.py)。如果一切顺利你会看到自动弹出一个Chrome浏览器窗口打开百度自动输入文字并搜索然后在控制台打印出标题最后浏览器关闭。恭喜你你的Selenium环境已经搭建成功如果运行失败最常见的错误信息就是驱动问题。根据错误提示回到上一步检查驱动版本匹配和路径配置。记住driver.quit()是关闭浏览器并释放资源而driver.close()只是关闭当前标签页。在脚本结束时务必调用driver.quit()这是一个好习惯。3. Selenium核心操作从元素定位到复杂交互环境搭好脚本能跑起来只是万里长征第一步。Selenium的核心能力在于对网页元素的精确操控。这部分的掌握程度直接决定了你自动化脚本的稳定性、可维护性和执行效率。我们把它拆解为几个关键环节。3.1 元素定位八种武器与最佳实践要让Selenium帮你点击一个按钮或输入一段文字你首先得告诉它“目标”在哪里。这就是元素定位。Selenium WebDriver提供了8种主要的定位策略通过By类调用ID (By.ID): 通过元素的id属性定位。id在理想情况下应该是页面内唯一的定位速度最快优先级最高。element driver.find_element(By.ID, “username”)Name (By.NAME): 通过元素的name属性定位。常用于表单元素。element driver.find_element(By.NAME, “password”)Class Name (By.CLASS_NAME): 通过元素的class属性定位。注意一个元素可能有多个class这里匹配的是完整的class字符串。element driver.find_element(By.CLASS_NAME, “btn-submit”)Tag Name (By.TAG_NAME): 通过标签名定位如input,a,div。通常用于查找一组同类元素。inputs driver.find_elements(By.TAG_NAME, “input”) # 返回一个列表Link Text (By.LINK_TEXT): 精确匹配超链接的完整文本内容。element driver.find_element(By.LINK_TEXT, “忘记密码”)Partial Link Text (By.PARTIAL_LINK_TEXT): 匹配超链接文本的一部分。element driver.find_element(By.PARTIAL_LINK_TEXT, “忘记”) # 也能找到“忘记密码”CSS Selector (By.CSS_SELECTOR): 使用CSS选择器语法定位功能非常强大和灵活。# 定位id为‘container’下的第一个class包含‘item’的div element driver.find_element(By.CSS_SELECTOR, “div#container div.item:first-child”)XPath (By.XPATH): 使用XML路径语言定位功能最强大可以遍历XML/HTML文档的任何节点但语法相对复杂。# 定位文本内容为‘登录’的按钮 element driver.find_element(By.XPATH, “//button[text()‘登录’]”)定位策略的选择心得优先级IDNameCSS SelectorXPath 其他。ID和Name是浏览器原生支持的最快定位方式。稳定性优先选择那些不会随页面布局或样式微调而改变的属性如业务逻辑相关的id、name或者具有特殊意义的>driver.implicitly_wait(10) # 单位是秒 # 后续所有find_element操作最多等待10秒 element driver.find_element(By.ID, “dynamic-element”)注意事项隐式等待是全局设置在整个driver生命周期都有效。但它不适用于判断元素的特定状态如可点击、可见。混合使用隐式等待和显式等待可能导致不可预料的超时行为一般建议在项目中只选用一种更推荐显式等待。显式等待 (WebDriverWait): 这是最强大、最推荐的等待方式。它为某个特定条件设置等待条件成立则立即继续否则在超时后抛出异常。它提供了丰富的“预期条件”expected_conditions。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 创建一个最长等待10秒的WebDriverWait对象 wait WebDriverWait(driver, 10) # 等待直到ID为‘submit-btn’的元素可被点击 submit_button wait.until(EC.element_to_be_clickable((By.ID, “submit-btn”))) submit_button.click() # 等待直到ID为‘result’的元素内出现特定文本 success_message wait.until(EC.text_to_be_present_in_element((By.ID, “result”), “操作成功”))常用的expected_conditions包括presence_of_element_located: 元素出现在DOM中不一定可见。visibility_of_element_located: 元素可见宽高大于0。element_to_be_clickable: 元素可见且可点击。title_contains: 标题包含某文字。alert_is_present: 出现警告框。等待策略最佳实践在项目中我通常会完全禁用隐式等待driver.implicitly_wait(0)然后全程使用显式等待。显式等待的代码意图更清晰“我在等什么”并且可以针对不同操作设置不同的超时时间效率更高。将常用的等待操作封装成函数或工具方法能极大提升代码的整洁度。3.3 浏览器操作与高级交互定位和等待解决了“找到并等到”元素的问题接下来就是“操作”元素和浏览器本身。基础操作输入文本element.send_keys(“your text”)点击element.click()清空输入框element.clear()获取元素属性/文本element.get_attribute(“href”),element.text提交表单element.submit()(通常用于form内的input type“submit”)浏览器导航打开网址driver.get(“url”)或driver.navigate().to(“url”)前进/后退driver.forward(),driver.back()刷新driver.refresh()窗口与框架iframe处理切换窗口当点击链接打开新标签页时需要切换句柄。main_window driver.current_window_handle # 获取当前窗口句柄 # 点击某个打开新窗口的链接... for handle in driver.window_handles: # 遍历所有窗口句柄 if handle ! main_window: driver.switch_to.window(handle) # 切换到新窗口 break # 操作新窗口... driver.close() # 关闭新窗口 driver.switch_to.window(main_window) # 切回原窗口切换iframe很多页面广告、登录框嵌套在iframe里必须先切换进去才能操作内部元素。# 通过id、name或索引切换 driver.switch_to.frame(“iframe_id”) # 操作iframe内的元素... driver.switch_to.default_content() # 切回主文档高级用户交互Actions API对于拖拽、悬停、组合按键等复杂操作需要使用ActionChains类。from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys actions ActionChains(driver) # 鼠标悬停 menu driver.find_element(By.ID, “menu”) actions.move_to_element(menu).perform() # 拖拽元素 source driver.find_element(By.ID, “draggable”) target driver.find_element(By.ID, “droppable”) actions.drag_and_drop(source, target).perform() # 组合按键例如CtrlA全选 input_box driver.find_element(By.ID, “textbox”) actions.key_down(Keys.CONTROL).send_keys(“a”).key_up(Keys.CONTROL).perform()执行JavaScript当Selenium提供的API无法满足某些特殊操作时如直接修改元素属性、滚动页面到特定位置可以直接执行JavaScript。# 滚动到页面底部 driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) # 修改元素样式 element driver.find_element(By.ID, “my-div”) driver.execute_script(“arguments[0].style.border ‘3px solid red’”, element) # 获取页面标题示例通常用driver.title即可 title driver.execute_script(“return document.title;”)4. 实战构建一个健壮的自动化测试用例了解了所有零件之后我们需要把它们组装成一个可靠的、可维护的自动化测试用例。一个好的测试脚本不仅仅是能跑通还要考虑异常处理、日志记录、可配置性和可读性。我们以自动化登录一个示例网站并验证登录成功为例来构建一个相对完整的脚本。4.1 用例设计与页面对象模型PO思想在开始编码前先进行简单的设计。我们将测试步骤分解打开登录页面。输入用户名。输入密码。点击登录按钮。验证是否跳转到成功页面或出现成功提示。直接将这些步骤写在主脚本里也可以但随着测试用例增多你会发现元素定位器、页面URL散落在各个脚本中一旦页面改动维护成本极高。因此引入页面对象模型Page Object Model, PO的思想至关重要。PO模式的核心是将每个页面封装成一个类页面的元素定位器和基本操作作为这个类的方法。测试脚本只调用这些方法不关心具体的定位细节。我们先创建一个简单的页面对象类LoginPage# login_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: # 1. 定位器将页面上所有需要操作的元素定位方式集中管理 URL “https://example.com/login” # 页面地址 USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.NAME, “password”) LOGIN_BUTTON (By.CSS_SELECTOR, “button[type‘submit’]”) SUCCESS_MESSAGE (By.CLASS_NAME, “alert-success”) def __init__(self, driver): self.driver driver self.wait WebDriverWait(self.driver, 10) # 2. 页面操作方法每个方法代表一个用户操作 def load(self): “”“打开登录页面”“” self.driver.get(self.URL) return self # 支持链式调用 def enter_username(self, username): “”“输入用户名”“” username_field self.wait.until(EC.presence_of_element_located(self.USERNAME_INPUT)) username_field.clear() username_field.send_keys(username) return self def enter_password(self, password): “”“输入密码”“” password_field self.wait.until(EC.visibility_of_element_located(self.PASSWORD_INPUT)) password_field.clear() password_field.send_keys(password) return self def click_login(self): “”“点击登录按钮”“” login_btn self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON)) login_btn.click() return self def get_success_message(self): “”“获取登录成功后的提示信息文本”“” # 等待成功信息出现并返回其文本 message_element self.wait.until(EC.visibility_of_element_located(self.SUCCESS_MESSAGE)) return message_element.text4.2 编写主测试脚本现在我们的主测试脚本变得非常清晰和简洁# test_login.py import unittest from selenium import webdriver from login_page import LoginPage class TestLogin(unittest.TestCase): “”“登录功能测试用例”“” def setUp(self): “”“每个测试方法执行前运行初始化驱动”“” # 这里可以配置浏览器选项如无头模式、窗口大小等 options webdriver.ChromeOptions() # options.add_argument(‘--headless’) # 无头模式不显示浏览器界面 # options.add_argument(‘--disable-gpu’) # options.add_argument(‘--window-size1920,1080’) self.driver webdriver.Chrome(optionsoptions) self.driver.implicitly_wait(0) # 禁用隐式等待使用显式等待 def tearDown(self): “”“每个测试方法执行后运行清理环境”“” # 截图如果测试失败 if hasattr(self, ‘_outcome‘): # 判断是否是unittest的运行结果 result self._outcome.result if result.errors or result.failures: self.driver.save_screenshot(“login_failure.png“) self.driver.quit() def test_successful_login(self): “”“测试成功登录”“” login_page LoginPage(self.driver) # 使用页面对象的方法链式调用让流程一目了然 actual_message (login_page.load() .enter_username(“valid_user”) .enter_password(“valid_pass”) .click_login() .get_success_message()) # 断言验证结果 expected_message “登录成功欢迎回来。” self.assertEqual(actual_message, expected_message, f“成功消息不符。实际‘{actual_message}’期望‘{expected_message}’”) def test_failed_login_with_wrong_password(self): “”“测试密码错误登录失败”“” # 这里可以扩展LoginPage增加获取错误提示信息的方法 # 步骤类似最后断言错误信息是否正确 pass if __name__ “__main__“: unittest.main(verbosity2) # 运行测试并显示详细信息这个脚本展示了几个关键点使用unittest框架提供了setUp初始化和tearDown清理方法以及断言assert是组织测试用例的标准方式。清晰的业务逻辑主测试方法里只有业务步骤没有底层定位细节可读性极高。异常处理与调试在tearDown中加入了失败截图功能这对于排查CI/CD环境下的失败用例至关重要。浏览器选项配置展示了如何通过ChromeOptions配置浏览器行为如无头模式--headless在服务器上运行非常有用。4.3 数据驱动测试上面的测试用例数据用户名、密码是硬编码的。在实际项目中我们往往需要用多组数据测试同一个流程。这时可以采用数据驱动测试。unittest本身支持但使用pytest框架配合pytest.mark.parametrize装饰器更为优雅。这里我们用unittest的subTest来演示import unittest from selenium import webdriver from login_page import LoginPage class TestLoginDataDriven(unittest.TestCase): def setUp(self): self.driver webdriver.Chrome() self.driver.implicitly_wait(0) def tearDown(self): self.driver.quit() # 测试数据 login_test_data [ (“valid_user”, “valid_pass”, True, “登录成功欢迎回来。”), (“invalid_user”, “valid_pass”, False, “用户名或密码错误。”), (“valid_user”, “”, False, “密码不能为空。”), ] def test_login_with_multiple_data(self): “”“使用多组数据测试登录”“” for username, password, should_succeed, expected_msg in self.login_test_data: with self.subTest(usernameusername, passwordpassword): # subTest用于区分每组数据 self.driver.get(“https://example.com/login”) # 每次循环回到登录页 login_page LoginPage(self.driver) login_page.enter_username(username) login_page.enter_password(password) login_page.click_login() # 根据预期结果进行不同断言 if should_succeed: actual_msg login_page.get_success_message() self.assertEqual(actual_msg, expected_msg) else: # 假设LoginPage有一个get_error_message方法 actual_msg login_page.get_error_message() self.assertEqual(actual_msg, expected_msg)通过数据驱动一个测试方法可以覆盖多个测试场景大大减少了代码重复提高了测试覆盖率。5. 进阶配置、问题排查与最佳实践当你掌握了基础操作并能编写完整的测试用例后就会遇到更实际的环境问题和效率挑战。这部分分享一些进阶配置和踩坑经验。5.1 浏览器选项Options与常见配置通过ChromeOptions、FirefoxOptions等对象你可以精细控制浏览器的启动行为。以下是一些常用配置from selenium import webdriver from selenium.webdriver.chrome.options import Options as ChromeOptions from selenium.webdriver.chrome.service import Service chrome_options ChromeOptions() # 1. 无头模式不显示GUI节省资源适用于服务器/CI环境 chrome_options.add_argument(‘--headlessnew’) # Selenium 4.8推荐使用‘new’ chrome_options.add_argument(‘--disable-gpu’) # Windows系统建议禁用GPU加速 # 2. 禁用沙盒和开发者模式警告某些Linux环境或Docker中可能需要 chrome_options.add_argument(‘--no-sandbox’) chrome_options.add_argument(‘--disable-dev-shm-usage’) # 解决共享内存空间不足问题 chrome_options.add_experimental_option(“excludeSwitches”, [“enable-logging”, “enable-automation“]) chrome_options.add_experimental_option(‘useAutomationExtension’, False) # 3. 用户数据目录使用特定用户配置文件可以保存登录态、Cookie等 # chrome_options.add_argument(r‘--user-data-dirC:\Users\YourName\AppData\Local\Google\Chrome\User Data’) # chrome_options.add_argument(‘--profile-directoryDefault’) # 4. 其他常用参数 chrome_options.add_argument(‘--window-size1920,1080’) # 设置初始窗口大小 chrome_options.add_argument(‘--disable-blink-featuresAutomationControlled’) # 尝试隐藏自动化特征部分反爬 chrome_options.add_argument(‘--disable-infobars’) # 禁用“Chrome正受到自动测试软件控制”的信息栏 # 5. 设置下载路径需要指定prefs prefs { “download.default_directory”: r“C:\Downloads”, # 下载目录 “download.prompt_for_download”: False, # 下载时不弹出确认窗口 “plugins.always_open_pdf_externally”: True # 直接下载PDF不在浏览器内打开 } chrome_options.add_experimental_option(“prefs”, prefs) # 初始化驱动传入options service Service(‘chromedriver_path‘) # 可选 driver webdriver.Chrome(serviceservice, optionschrome_options)关于无头模式的注意事项无头模式下一些依赖于视觉渲染或特定浏览器特性的操作如某些复杂的Canvas验证码识别可能会失败。在调试阶段建议先关闭无头模式确保脚本逻辑正确后再开启。5.2 常见问题排查与解决技巧在自动化过程中你会遇到各种千奇百怪的问题。这里整理一个常见问题速查表问题现象可能原因排查与解决思路SessionNotCreatedException: ... This version of ChromeDriver only supports Chrome version ...浏览器驱动与浏览器版本不匹配。1. 检查Chrome版本。2. 下载对应版本的ChromeDriver。3. 使用Selenium ManagerSelenium 4.6或手动指定正确驱动路径。NoSuchElementException元素定位失败。1.等待问题元素尚未加载。增加显式等待WebDriverWait。2.定位器问题定位表达式写错或元素属性已改变。用DevTools重新检查元素优化定位器。3.iframe/Shadow DOM元素在iframe或Shadow DOM内需先切换上下文。ElementNotInteractableException元素存在但不可交互如被遮挡、不可见、禁用。1.不可见等待元素可见EC.visibility_of。2.被遮挡等待遮挡物消失或使用ActionChains点击。3.禁用状态检查元素disabled属性。StaleElementReferenceException元素已过时页面刷新或AJAX更新后之前找到的元素引用失效。黄金法则不要在变量中长期保存元素对象尤其是动态页面。每次操作前重新查找或使用expected_conditions来等待和查找合二为一。脚本在本地运行成功在服务器/CI上失败环境差异浏览器版本、屏幕分辨率、无头模式、资源加载速度。1. 统一环境使用Docker容器固化测试环境。2. 增加等待时间服务器可能比本地慢。3. 添加失败截图和日志在tearDown或异常捕获中截图、打印页面源码和当前URL。4. 检查无头模式兼容性。被网站检测为自动化脚本反爬浏览器指纹或WebDriver特征被识别。1. 使用options.add_argument(‘--disable-blink-featuresAutomationControlled’)。2. 使用driver.execute_cdp_cmd执行CDP命令修改navigator.webdriver属性较高级。3. 考虑使用更接近真人行为的工具如Playwright其CDP覆盖更全。注意请严格遵守网站的使用条款仅用于合法合规的自动化测试。调试技巧截图driver.save_screenshot(‘error.png’)是定位问题的第一利器。打印页面源码/当前URLprint(driver.page_source[:2000])或print(driver.current_url)看看页面是否如你所想。使用time.sleep临时调试在怀疑是等待问题的地方临时加一个sleep如果加了就成功说明需要优化等待条件。启用浏览器日志初始化时添加options.set_capability(‘goog:loggingPrefs’, {‘browser’: ‘ALL’})之后用driver.get_log(‘browser’)获取日志有助于分析JS错误或网络问题。5.3 组织大型项目与持续集成当你的自动化脚本越来越多就需要考虑项目结构和持续集成CI。项目结构建议your_automation_project/ ├── config/ │ └── settings.py # 配置文件存放URL、账号、超时时间等 ├── pages/ # 页面对象类 │ ├── __init__.py │ ├── login_page.py │ ├── home_page.py │ └── ... ├── tests/ # 测试用例 │ ├── __init__.py │ ├── test_login.py │ ├── test_order.py │ └── ... ├── utils/ # 工具函数 │ ├── __init__.py │ ├── logger.py # 日志模块 │ └── helpers.py # 通用帮助函数 ├── reports/ # 测试报告输出目录 ├── drivers/ # 存放各浏览器驱动可选通常用Selenium Manager ├── requirements.txt # Python依赖列表 └── run_tests.py # 主运行脚本与CI/CD工具集成 你可以将测试脚本集成到Jenkins、GitLab CI、GitHub Actions等CI/CD平台中。核心步骤通常包括配置环境在CI机器上安装Python、浏览器如使用无头Chrome和依赖pip install -r requirements.txt。执行测试运行你的测试运行器如pytest tests/ --htmlreports/report.html。收集结果收集测试报告如pytest-html生成的HTML报告、日志和失败截图。通知根据测试结果成功/失败发送邮件或消息通知。一个简单的GitHub Actions配置示例.github/workflows/test.ymlname: Selenium Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: ‘3.10’ - name: Install dependencies run: | pip install -r requirements.txt sudo apt-get update sudo apt-get install -y chromium-browser chromium-chromedriver # Linux安装Chrome - name: Run tests run: | python -m pytest tests/ -v --htmlreports/report.html --self-contained-html - name: Upload test report uses: actions/upload-artifactv3 if: always() # 无论测试成功失败都上传报告 with: name: selenium-test-report path: reports/5.4 Selenium vs. Playwright如何选择近年来微软开源的Playwright异军突起常被拿来与Selenium比较。简单说一下我的看法Selenium生态成熟标准统一语言支持广。它是W3C WebDriver标准的实现历史悠久社区庞大几乎所有你能想到的浏览器和语言都有支持。如果你需要支持IE等老旧浏览器或者团队技术栈多样Java、Python、C#混用Selenium是稳妥的选择。它的学习资料和解决方案也最丰富。Playwright后起之秀性能强劲开发体验好。它由原Puppeteer团队开发提供了更现代、更强大的API。其最大优势在于“自动等待”元素可操作时自动执行无需手动写等待、强大的网络拦截与模拟能力、以及原生支持移动端模拟和跨浏览器测试。它的执行速度通常比Selenium快且脚本更简洁。选择建议如果你是新手从Selenium开始学习成本相对较低资料多遇到的问题基本都能搜到答案。理解了Selenium的核心概念定位、等待、驱动再学Playwright会非常快。如果你的项目是全新的且主要面向现代浏览器Chrome、Firefox、Edge、Safari追求更快的执行速度和更简洁的代码可以考虑直接上Playwright。如果项目需要支持IE或特定的远程Grid环境如Selenium GridSelenium目前仍是更成熟的选择。两者并非完全替代关系很多团队会根据不同场景混合使用。掌握其核心思想——即如何通过程序控制浏览器——才是最重要的。