1. 项目概述为什么你需要关注Selenium 4.0如果你正在用Python做自动化测试或者想从网页上抓点数据那你肯定绕不开Selenium。这个工具从诞生到现在几乎成了浏览器自动化的代名词。我最早接触Selenium 2.0的时候还得自己折腾各种驱动路径写一堆find_element_by_*的定位语句。后来3.0版本稳定了几年大家也都用顺手了。但去年Selenium 4.0正式发布我第一时间升级项目后发现变化还真不小——有些是更方便了有些则是彻底改变了写法。简单来说Selenium 4.0不是一次小修小补的更新。它引入了一套全新的、更符合现代开发习惯的API比如相对定位器Relative Locators让你不用再死磕XPath对W3C WebDriver协议的全面支持让跨浏览器行为更一致还有原生的窗口和标签页管理、改进的DevTools集成等等。但最大的变化可能是它彻底废弃了老版本的find_element_by_id、find_element_by_xpath这类方法强制要求使用新的find_element(By.ID, “id”)格式。这个改动让很多老脚本直接报错但也逼着我们去拥抱更规范、更强大的写法。所以这篇内容就是为你准备的无论你是刚入门的新手还是从3.0迁移过来的老手。我会带你从零开始把Selenium 4.0在Python里的新玩法、新特性以及那些升级路上必踩的坑都掰开揉碎了讲清楚。目标就一个让你看完就能上手写出更健壮、更高效的自动化脚本。2. 环境搭建与核心依赖安装万事开头难但环境装对了后面就顺了。Selenium 4.0对Python版本和浏览器驱动有新的要求别再用老一套了。2.1 Python环境与Selenium库安装首先确保你的Python版本在3.7或以上。这是Selenium 4.0的最低要求。我推荐直接用Python 3.8或3.9兼容性和稳定性都经过大量项目验证。检查版本很简单打开命令行输入python --version就行。安装Selenium库我强烈建议使用pip并指定版本。别直接用pip install selenium那样可能会装上一个不太稳定的最新小版本。最稳妥的做法是安装4.0系列的一个稳定版本比如4.1.0或4.2.0。pip install selenium4.2.0这里有个小技巧如果你在公司的内网环境或者依赖包下载很慢可以试试清华或阿里的镜像源。命令会变成这样pip install selenium4.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以写个两行代码验证一下from selenium import webdriver print(webdriver.__version__)如果能正常打印出版本号比如4.2.0说明库安装成功了。注意有些教程会教你用虚拟环境venv对于新手或者做一次性脚本其实可以跳过。但如果你同时在做好几个项目或者怕包冲突那用虚拟环境是个好习惯。创建并激活虚拟环境的命令是python -m venv myenv和source myenv/bin/activateLinux/Mac或myenv\Scripts\activateWindows。2.2 浏览器驱动的自动化管理WebDriver Manager这是Selenium 4.0时代我最推荐的一个升级以前我们得手动去Chrome或Firefox官网下载对应版本的驱动chromedriver, geckodriver还得把驱动放到系统PATH里或者指定路径。版本对不上就报错非常麻烦。现在有了webdriver-manager这个第三方库它可以自动检测你电脑上安装的浏览器版本并下载匹配的驱动。简直是省心利器。首先安装它pip install webdriver-manager然后在代码里使用就变得极其简单。以Chrome为例from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager # 自动下载并使用匹配的ChromeDriver service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) driver.get(https://www.baidu.com)运行这段代码webdriver-manager会检查你的Chrome版本去它的仓库找到对应的chromedriver下载并缓存到本地然后交给Selenium使用。以后再运行只要浏览器版本没变它就会直接用缓存的驱动速度很快。对于Firefox和Edge用法类似# Firefox from webdriver_manager.firefox import GeckoDriverManager service Service(GeckoDriverManager().install()) driver webdriver.Firefox(serviceservice) # Microsoft Edge from webdriver_manager.microsoft import EdgeChromiumDriverManager service Service(EdgeChromiumDriverManager().install()) driver webdriver.Edge(serviceservice)实操心得webdriver-manager默认从GitHub下载驱动国内网络可能不稳定。你可以通过设置环境变量WDM_SSL_VERIFY0来关闭SSL验证不推荐长期使用或者更一劳永逸的方法是配置一个国内镜像源。不过根据我的经验它的缓存机制做得不错第一次成功下载后后续基本不会有网络问题。2.3 集成开发环境IDE的选择与配置写Python脚本一个好用的编辑器能事半功倍。新手我推荐VSCode老手可能更喜欢PyCharm。VSCode配置要点安装Python扩展Microsoft官方出品。在项目文件夹下按CtrlShiftP输入 “Python: Select Interpreter”选择你安装Python的解释器。安装Pylance或Jedi扩展来获得更好的代码补全和提示。当你输入webdriver.时如果能自动弹出Chrome、Firefox和By这些类和方法就说明配置成功了。PyCharm配置要点新建项目时注意选择正确的Python解释器Interpreter。PyCharm通常能自动识别项目依赖。如果不行在File - Settings - Project - Python Interpreter里点“”号搜索并添加selenium和webdriver-manager。无论用哪个关键是要能顺畅地运行和调试。我习惯在写复杂定位或者遇到问题时在关键行打上断点然后使用调试模式一步步看变量状态这比盲目加print语句高效得多。3. Selenium 4.0核心API与定位策略详解环境准备好了我们来啃最硬的骨头元素定位。这是Selenium脚本的基石定位不准一切操作都白搭。Selenium 4.0在这里做了重大革新。3.1 新旧定位API对比与迁移在Selenium 3和更早的版本我们是这样定位元素的# 旧版写法Selenium 4.0 中已废弃 driver.find_element_by_id(“kw”) driver.find_element_by_class_name(“s_ipt”) driver.find_element_by_xpath(“//input[id‘kw]”)这些方法直观但属于“便捷方法”在Selenium 4.0里已经被标记为废弃deprecated。虽然目前还能用但会抛出警告未来版本可能会彻底移除。新的、官方推荐的写法是统一使用find_element和find_elements方法配合By这个枚举类。# 新版标准写法Selenium 4.0 推荐 from selenium.webdriver.common.by import By element driver.find_element(By.ID, “kw”) elements driver.find_elements(By.CLASS_NAME, “s_ipt”) element driver.find_element(By.XPATH, “//input[id‘kw]”)By类提供了所有定位方式的常量ID,NAME,CLASS_NAME,TAG_NAME,LINK_TEXT,PARTIAL_LINK_TEXT,CSS_SELECTOR,XPATH。为什么这么改一致性所有定位方式通过同一个方法入口API设计更统一、清晰。可读性By.ID明确指出了定位策略代码意图更明显。灵活性便于封装和传递定位策略。比如你可以写一个函数接收By和locator作为参数。迁移老脚本时你需要做的就是批量替换。把所有的find_element_by_*和find_elements_by_*改成find_element(By.*, ...)和find_elements(By.*, ...)的形式。3.2 八大定位策略深度解析与选用指南光知道写法不够还得知道什么时候用什么。我把这8种策略分成三个梯队第一梯队首选稳定高效By.ID一个页面中元素的id属性理论上是唯一的。如果开发给了id无脑用它速度最快最稳定。search_box driver.find_element(By.ID, “search”)By.NAMEname属性也常用于表单元素通常也比较稳定。仅次于ID的选择。username_input driver.find_element(By.NAME, “username”)第二梯队常用但需注意By.CSS_SELECTOR功能强大语法简洁解析速度比XPath快。适合定位没有ID/NAME但具有特定样式或结构的元素。# 定位class为‘btn-primary’的按钮 button driver.find_element(By.CSS_SELECTOR, “.btn-primary”) # 定位id为‘form’下的第一个input子元素 input driver.find_element(By.CSS_SELECTOR, “#form input:first-child”)By.XPATH功能最强大可以遍历XML/HTML文档的任何节点。当其他定位方式都失效时它是最后的武器。但写起来复杂执行速度相对较慢。# 定位文本为“登录”的按钮 login_btn driver.find_element(By.XPATH, “//button[text()‘登录]”) # 定位包含特定class的div div driver.find_element(By.XPATH, “//div[contains(class, ‘container’)]”)注意事项尽量避免使用绝对路径以/开头的XPath比如/html/body/div[3]/div[2]/form/input。这种路径极度脆弱页面结构稍有变动就会失效。始终使用相对路径和属性结合的方式。第三梯队特定场景使用By.CLASS_NAME直接通过class属性定位。但一个元素的class可能有多个值用空格分隔且class通常不唯一容易定位到多个元素。By.TAG_NAME通过标签名定位如input,div,a。通常用于获取某一类元素的集合。all_links driver.find_elements(By.TAG_NAME, “a”) # 获取页面所有链接By.LINK_TEXT / By.PARTIAL_LINK_TEXT专门用于定位超链接 (a标签)通过链接的完整文本或部分文本定位。# 精确匹配链接文本 exact_link driver.find_element(By.LINK_TEXT, “用户协议”) # 模糊匹配链接文本包含“用户”二字 partial_link driver.find_element(By.PARTIAL_LINK_TEXT, “用户”)选用黄金法则有ID用ID。没ID看NAME。表单元素多看看CSS_SELECTOR。复杂结构或文本匹配用XPATH。链接文本匹配用LINK_TEXT系列。尽量避免使用不稳定的CLASS_NAME和宽泛的TAG_NAME做精确查找。3.3 革命性新特性相对定位器这是Selenium 4.0引入的一个非常酷的功能。想象一下你想点击一个“提交”按钮但这个按钮没有唯一的ID或NAME它的位置总是在某个输入框的下方。以前你可能得写一段复杂的XPath来描述这种相对位置关系。现在用相对定位器就简单了。相对定位器通过with_tag_name方法结合above(),below(),to_left_of(),to_right_of(),near()这些方法来描述元素间的空间位置关系。from selenium.webdriver.support.relative_locator import locate_with # 假设有一个已知的元素比如一个输入框 username_field driver.find_element(By.ID, “username”) # 定位在这个输入框下方的“提交”按钮 # 意思是找一个标签名为‘button’的元素它在username_field元素的下方 submit_button driver.find_element(locate_with(By.TAG_NAME, “button”).below(username_field)) # 定位在输入框右侧的“忘记密码”链接 forgot_link driver.find_element(locate_with(By.TAG_NAME, “a”).to_right_of(username_field))适用场景与局限场景非常适合定位那些位置固定但属性不唯一的UI组件比如表格操作栏的按钮、表单中按顺序排列的字段。局限它的判断是基于元素在页面可视化视图中的位置而不是DOM结构。如果页面布局是响应式的或者使用了position: absolute/fixed等脱离文档流的定位方式可能会不准确。在实际使用前最好在目标页面上测试一下。4. 等待机制从“隐式”到“显式”的进化自动化脚本运行快但网页加载和元素渲染需要时间。处理不好等待就会抛出NoSuchElementException找不到元素。Selenium提供了两种主要的等待方式但在Selenium 4.0的实践中观念已经转变。4.1 隐式等待的功与过隐式等待Implicit Wait是设置一个全局的等待时间在查找任何元素时如果元素没有立即出现WebDriver会轮询DOM一段时间你设置的时长直到找到元素或超时。driver.implicitly_wait(10) # 单位秒这行代码的意思是在接下来的所有find_element操作中如果元素没找到最多等10秒每隔一段时间找一次。它的“过”全局影响设置后对所有的find_element和find_elements都生效有时你并不想等那么久比如确认某个元素不存在。只针对元素查找它不等待元素的“状态”。比如元素找到了但可能是不可点击disabled或不可见hidden的你的click()操作依然会失败。与显式等待混用可能导致不可预知的长等待。官方文档也不推荐混用。因此在现代Selenium最佳实践中建议完全避免使用隐式等待或者仅将其设置为一个很小的值如2-3秒作为基础保障而将核心的等待逻辑交给显式等待。4.2 显式等待现代脚本的等待标准显式等待Explicit Wait是针对某个特定条件进行等待。你可以指定等待的最大时长以及检查条件的频率轮询间隔。它更精确更灵活。Selenium 4.0中我们使用WebDriverWait类和expected_conditions模块通常简写为EC。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 创建一个WebDriverWait对象设置最大等待时间10秒 wait WebDriverWait(driver, 10) # 等待直到ID为‘myButton’的元素可以被点击 element wait.until(EC.element_to_be_clickable((By.ID, “myButton”))) element.click() # 等待直到ID为‘result’的元素内部出现特定文本 wait.until(EC.text_to_be_present_in_element((By.ID, “result”), “操作成功”)) # 等待直到页面标题包含某个关键词 wait.until(EC.title_contains(“Dashboard”))核心优势条件化不仅仅是“元素存在”而是“元素可点击”、“元素可见”、“文本出现”等具体状态。局部化只在你需要的地方等待不影响其他操作。返回元素until方法在条件满足后会返回符合条件的元素对象你可以直接链式调用代码更简洁。wait.until(EC.presence_of_element_located((By.NAME, “q”))).send_keys(“Selenium 4”)4.3 常用 Expected Conditions 条件解析expected_conditions模块提供了大量预定义的条件以下是几个最常用的条件方法名说明典型应用场景presence_of_element_located元素出现在DOM树中不一定可见等待动态加载的组件被添加到页面visibility_of_element_located元素出现在DOM中并且可见宽高大于0等待加载动画消失真正的内容显示出来element_to_be_clickable元素可见并且可点击enabled点击按钮、链接前的等待text_to_be_present_in_element元素内部包含指定的文本验证操作成功后的提示信息title_is/title_contains页面标题完全匹配/包含指定文本等待页面跳转完成alert_is_present等待警告框Alert出现处理弹窗提示自定义等待条件 如果内置条件不满足需求你可以自定义一个函数只要这个函数返回True条件满足或一个非False的值如找到的元素条件不满足时返回False。def element_has_css_class(locator, css_class): def _predicate(driver): element driver.find_element(*locator) if css_class in element.get_attribute(“class”).split(): return element return False return _predicate # 使用自定义条件 wait.until(element_has_css_class((By.ID, “status”), “completed”))实操心得在我的项目中几乎90%的等待都使用显式等待。我会为每个需要等待的操作如点击、输入后等待结果单独设置WebDriverWait。一个常见的模式是在页面跳转或重大操作后使用一个“页面加载完成”的等待条件如等待某个关键元素出现然后再进行后续操作。这比死板的time.sleep()要可靠和高效得多。5. 高级交互与多窗口/标签页管理掌握了定位和等待你已经能完成大部分基础操作了。接下来我们看看更高级的交互以及如何驾驭现代浏览器多标签页的复杂场景。5.1 键盘与鼠标的高级操作ActionChains有些交互不是简单click()和send_keys()能完成的比如拖拽、悬停、右键菜单、组合键等。这就需要ActionChains类。悬停Hover这是最常见的场景很多下拉菜单需要在鼠标悬停时才会显示。from selenium.webdriver.common.action_chains import ActionChains menu driver.find_element(By.ID, “dropdownMenu”) sub_menu driver.find_element(By.LINK_TEXT, “子选项”) # 创建ActionChains对象将鼠标移动到menu上然后点击出现的子选项 actions ActionChains(driver) actions.move_to_element(menu).click(sub_menu).perform() # 注意所有动作链需要以 .perform() 结尾才会执行拖拽Drag and Dropsource driver.find_element(By.ID, “draggable”) target driver.find_element(By.ID, “droppable”) actions ActionChains(driver) actions.drag_and_drop(source, target).perform() # 或者分步操作 # actions.click_and_hold(source).move_to_element(target).release().perform()组合键操作from selenium.webdriver.common.keys import Keys text_field driver.find_element(By.ID, “textField”) actions ActionChains(driver) # 模拟 CtrlA (全选) actions.key_down(Keys.CONTROL).send_keys(“a”).key_up(Keys.CONTROL).perform() # 模拟输入文本并回车 actions.send_keys_to_element(text_field, “Hello World” Keys.ENTER).perform()右键点击Context Clickelement driver.find_element(By.ID, “contextMenu”) actions ActionChains(driver) actions.context_click(element).perform() # 之后可能需要用 driver.switch_to.alert 或 键盘操作来处理右键菜单注意事项ActionChains的动作是存储在队列中的调用.perform()时才会按顺序执行。有些复杂的连续操作比如拖拽到某个坐标点在 headless 模式无头模式下可能行为异常需要进行额外测试。5.2 多窗口与多标签页的切换策略现代Web应用经常在新窗口或新标签页打开链接。Selenium 4.0 提供了更清晰的原生API来处理这种情况。核心概念窗口句柄Window Handle每个浏览器窗口或标签页都有一个唯一的标识符称为句柄handle。driver.current_window_handle获取当前活动窗口的句柄driver.window_handles获取所有已打开窗口的句柄列表按打开顺序排序。标准操作流程点击一个会打开新窗口/标签页的链接。获取所有窗口句柄。切换到新窗口。在新窗口中进行操作。关闭新窗口切换回原窗口。# 1. 记录原始窗口句柄 original_window driver.current_window_handle # 2. 点击打开新标签页的链接例如一个target“_blank”的链接 driver.find_element(By.LINK_TEXT, “在新窗口打开”).click() # 3. 等待新窗口出现并获取所有句柄 WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2)) all_windows driver.window_handles # 4. 循环找到新窗口的句柄不是原始窗口的那个 for window_handle in all_windows: if window_handle ! original_window: driver.switch_to.window(window_handle) break # 5. 现在driver的操作上下文已经在新标签页了 print(“新页面标题”, driver.title) # ... 在新页面执行操作 ... # 6. 关闭新标签页 driver.close() # 7. 切换回原始窗口 driver.switch_to.window(original_window)Selenium 4.0 新APIdriver.switch_to.new_window(‘tab’|‘window’)这个新方法允许你直接创建一个新的标签页或窗口并自动切换到它。这在需要主动打开新空白页面的场景下非常方便。# 打开一个新标签页并切换过去 driver.switch_to.new_window(‘tab’) driver.get(“https://www.example.com”) # 打开一个新窗口并切换过去 driver.switch_to.new_window(‘window’)常见问题排查最常遇到的坑就是切换窗口的时机不对。如果点击后立即尝试获取window_handles可能新窗口还没完全打开导致列表里只有一个句柄。务必使用显式等待EC.number_of_windows_to_be()来确保新窗口已经打开这是最稳健的做法。5.3 处理JavaScript弹窗与浏览器对话框网页上的弹窗主要有三种Alert警告框、Confirm确认框、Prompt提示框。Selenium 使用driver.switch_to.alert来获取当前弹窗对象进行处理。# 触发一个Confirm框 driver.find_element(By.ID, “btnConfirm”).click() # 等待弹窗出现并切换到它 WebDriverWait(driver, 5).until(EC.alert_is_present()) alert driver.switch_to.alert # 获取弹窗文本 print(alert.text) # 点击“确认” alert.accept() # 或者点击“取消” # alert.dismiss() # 对于Prompt框还可以输入文本 # alert.send_keys(“输入的内容”) # alert.accept()文件上传对话框 文件上传 (input type“file”) 的处理很简单不要尝试用Selenium去操作系统级别的文件选择对话框。直接找到这个input元素用send_keys()传入文件的本地绝对路径即可。file_input driver.find_element(By.CSS_SELECTOR, “input[type‘file]”) file_input.send_keys(“/Users/yourname/Desktop/test_image.jpg”)系统对话框会自动跳过文件路径会被直接填入。这是唯一推荐的处理文件上传的方式。6. 实战编写一个健壮的Web自动化测试脚本理论讲得再多不如动手写一个。我们来模拟一个经典的电商场景打开购物网站搜索商品将第一个商品加入购物车然后验证购物车数量。我们将运用前面讲的所有知识点并特别注意异常处理和日志记录让脚本真正“健壮”。6.1 场景设计与脚本骨架目标自动化测试“在某电商平台搜索‘Python书籍’并添加商品到购物车”的核心流程。步骤启动浏览器访问电商网站首页。定位搜索框输入关键词“Python书籍”并提交搜索。等待搜索结果页面加载定位第一个商品。点击进入商品详情页。在详情页点击“加入购物车”按钮。等待操作成功的提示并验证购物车图标上的数量是否增加。关闭浏览器。脚本骨架含基础结构from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager import logging import time # 配置日志方便调试和记录运行过程 logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(levelname)s - %(message)s’) logger logging.getLogger(__name__) class ECommerceCartTest: def __init__(self): # 使用WebDriver Manager管理驱动 service Service(ChromeDriverManager().install()) # 初始化浏览器选项可选无头模式、忽略SSL错误等 options webdriver.ChromeOptions() # options.add_argument(‘--headless’) # 如需无头模式取消注释 options.add_argument(‘--ignore-certificate-errors’) options.add_argument(‘--start-maximized’) # 启动时最大化窗口 self.driver webdriver.Chrome(serviceservice, optionsoptions) self.wait WebDriverWait(self.driver, 15) # 创建一个全局的显式等待对象 logger.info(“浏览器启动成功”) def teardown(self): 测试结束后的清理工作 time.sleep(2) # 为了肉眼观察结果稍作停留 self.driver.quit() logger.info(“浏览器已关闭”) def test_add_to_cart(self): 核心测试流程 try: self.step_1_open_homepage() self.step_2_search_product() self.step_3_go_to_product_detail() self.step_4_add_to_cart_and_verify() logger.info(“测试用例执行成功”) except Exception as e: logger.error(f“测试执行过程中发生异常{e}”, exc_infoTrue) # 可以在异常时截图便于排查 self.driver.save_screenshot(‘error_screenshot.png’) raise # 重新抛出异常让外部知道测试失败 # 接下来的方法将实现各个步骤 def step_1_open_homepage(self): pass def step_2_search_product(self): pass # … 其他步骤方法 if __name__ “__main__”: test ECommerceCartTest() try: test.test_add_to_cart() finally: test.teardown()6.2 分步实现与异常加固现在我们来填充每个步骤的细节并加入足够的等待和异常处理。步骤1打开首页def step_1_open_homepage(self): url “https://www.example-mall.com” # 请替换为实际网址 self.driver.get(url) # 等待页面关键元素如Logo或搜索框出现确保首页加载完成 self.wait.until(EC.presence_of_element_located((By.ID, “site-logo”))) logger.info(f“已打开首页{url}”)步骤2搜索商品这里我们假设搜索框的ID是search搜索按钮的ID是search-button。def step_2_search_product(self): search_keyword “Python书籍” # 定位搜索框并输入关键词 search_box self.wait.until(EC.element_to_be_clickable((By.ID, “search”))) search_box.clear() # 清空可能存在的默认文本 search_box.send_keys(search_keyword) logger.info(f“已在搜索框输入{search_keyword}”) # 方式一点击搜索按钮 search_button self.driver.find_element(By.ID, “search-button”) search_button.click() # 方式二或者直接按回车键如果页面支持 # search_box.send_keys(Keys.RETURN) # 等待搜索结果页面加载。通常搜索后会有结果列表或加载提示。 # 我们等待一个代表结果加载完成的元素比如第一个商品项的出现。 self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, “.product-item”))) logger.info(“搜索结果页面加载完成”)步骤3进入第一个商品详情页def step_3_go_to_product_detail(self): # 定位第一个商品链接。这里假设商品链接在 class‘product-name’ 的a标签里。 # 使用 find_elements 获取列表取第一个。 first_product_link self.wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, “.product-item .product-name a”)) )[0] # 取列表中的第一个元素 product_title first_product_link.text logger.info(f“即将进入商品详情页{product_title}”) # 记录当前窗口句柄因为有些网站会在新标签页打开详情 original_window self.driver.current_window_handle first_product_link.click() # 判断是否打开了新窗口 WebDriverWait(self.driver, 10).until(EC.number_of_windows_to_be(2)) for window_handle in self.driver.window_handles: if window_handle ! original_window: self.driver.switch_to.window(window_handle) break # 等待新窗口详情页的某个关键元素加载比如“加入购物车”按钮 self.wait.until(EC.presence_of_element_located((By.ID, “add-to-cart-button”))) logger.info(“已切换到商品详情页”)步骤4加入购物车并验证def step_4_add_to_cart_and_verify(self): # 定位并点击“加入购物车”按钮 add_to_cart_button self.wait.until( EC.element_to_be_clickable((By.ID, “add-to-cart-button”)) ) add_to_cart_button.click() logger.info(“已点击‘加入购物车’按钮”) # 等待操作成功的反馈。可能是Toast提示、页面跳转或购物车数量更新。 # 场景A等待成功提示信息出现 success_message_locator (By.CLASS_NAME, “alert-success”) # 场景B更常见的是验证购物车图标上的数量从0变成1 # 假设购物车图标显示数量的元素ID是 ‘cart-count’ cart_count_locator (By.ID, “cart-count”) # 我们采用场景B并加入重试机制 def cart_count_updated(expected_count): 自定义等待条件检查购物车数量是否更新为指定值 try: count_element self.driver.find_element(*cart_count_locator) return int(count_element.text) expected_count except (ValueError, NoSuchElementException): return False # 假设加入购物车前数量是0加入后变成1 self.wait.until(lambda driver: cart_count_updated(1)) final_count self.driver.find_element(*cart_count_locator).text logger.info(f“验证成功购物车商品数量已更新为{final_count}”) # 关闭当前商品详情页标签页回到原搜索页可选 self.driver.close() self.driver.switch_to.window(self.driver.window_handles[0])6.3 运行、调试与日志分析将上述代码片段组合起来就形成了一个完整的、具有一定健壮性的测试脚本。运行它观察控制台日志。调试技巧慢放在关键操作前后添加time.sleep(1)方便肉眼观察浏览器状态。截图在异常捕获块或关键检查点使用driver.save_screenshot(‘step1.png’)保存截图。打印页面源码当定位不到元素时打印driver.page_source查看当前实际的HTML结构确认元素是否存在、属性是否正确。使用浏览器开发者工具在脚本运行中非headless模式手动按F12打开开发者工具使用元素检查器Inspector查看元素属性验证你的定位器是否准确。日志分析 我们配置的日志会记录每个步骤的开始和结束。如果测试失败日志中的异常信息exc_infoTrue会打印堆栈跟踪和自动保存的错误截图是排查问题的第一手资料。通过时间戳你也能看出哪个步骤耗时过长可能是网络或页面性能的瓶颈。这个实战例子涵盖了从环境准备、元素定位、等待、多窗口处理到异常处理和日志记录的完整流程。你可以以此为模板根据你要自动化的具体网站修改定位器和步骤逻辑构建属于自己的自动化脚本。记住没有一劳永逸的定位器网页结构一变脚本就可能失效因此良好的日志和错误处理机制是维护自动化脚本的关键。