1. 项目概述为什么你需要掌握Selenium如果你正在用Python做自动化测试或者数据抓取尤其是面对那些需要登录、有复杂交互或者大量JavaScript渲染的现代网页那你大概率已经听说过或者正在被Selenium折磨。我最早接触Selenium是在一个电商价格监控的项目里当时需要模拟用户登录、翻页、点击商品详情用传统的requestsBeautifulSoup组合几乎寸步难行页面数据全是JS动态加载的。从那时起Selenium就成了我工具箱里的常客从简单的表单提交到复杂的全流程自动化几乎都能搞定。简单来说Selenium是一个用于Web应用程序自动化测试的工具套件但它早已超出了测试的范畴成为了Python爬虫和RPA机器人流程自动化领域的一把利器。它的核心价值在于能驱动一个真实的浏览器如Chrome、Firefox去执行操作你写的代码就像一双无形的手可以点击、输入、滚动、跳转完全模拟真人操作。这意味着只要是人在浏览器里能看到的、能操作的理论上Selenium都能帮你自动化完成。对于初学者可能会被它复杂的配置和偶尔的不稳定吓退对于有经验的开发者又可能纠结于执行效率和多任务并发。这篇内容我会把我这些年用Selenium趟过的坑、总结的技巧从环境搭建到高级应用毫无保留地拆解给你看。无论你是想写个自动签到脚本还是构建一个企业级的自动化测试框架这里都有你需要的答案。2. 环境搭建与核心组件拆解很多人第一步就卡在了环境安装上不是驱动不对就是版本冲突。其实只要理清几个核心组件的关系一切都会清晰起来。2.1 核心三件套库、浏览器、驱动Selenium的工作流程可以理解为一个“三角关系”你的Python代码客户端通过Selenium库发送指令指令经由一个叫“WebDriver”的驱动程序传递给真实的浏览器浏览器执行后再把结果通过驱动返回给代码。Python Selenium库这就是你用pip install selenium安装的东西。它提供了一套Python语言的API让你能用Python代码去控制浏览器。它负责把driver.find_element(By.ID, “kw”).click()这样的Python调用转换成WebDriver协议能理解的HTTP请求。浏览器Chrome、Firefox、Edge等。这是实际执行操作和渲染页面的“演员”。你需要确保本地安装了它。浏览器驱动这是连接库和浏览器的“翻译官”和“传令兵”。每个浏览器都有自己专属的驱动比如Chrome的叫ChromeDriverFirefox的叫geckodriver。这是最容易出问题的地方。驱动版本必须和你的浏览器大版本号基本匹配。例如Chrome 120.x 通常需要ChromeDriver 120.x。注意绝对不要从一些不明来源的网站下载驱动。最安全的方式是直接从浏览器厂商的官方渠道获取。对于ChromeDriver可以去Chrome for Testing的官方仓库对于geckodriver去GitHub的Mozilla releases页面。2.2 一步到位的环境配置实操理论说再多不如动手配一次。我们以最主流的Chrome浏览器为例演示最稳妥的安装流程。第一步安装Selenium库打开你的命令行CMD、PowerShell或终端执行以下命令。建议使用虚拟环境。pip install selenium如果速度慢可以加上国内镜像源例如pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple第二步安装浏览器驱动手动版打开Chrome浏览器在地址栏输入chrome://settings/help查看你的Chrome版本号比如 121.0.6167.85。访问https://googlechromelabs.github.io/chrome-for-testing/这个官方站点。这里提供了与Chrome稳定版匹配的驱动比传统下载页更可靠。找到与你Chrome主版本号如121匹配的chromedriver下载链接。根据你的操作系统win32, win64, mac-arm64, mac-x64, linux64下载对应的压缩包。解压压缩包你会得到一个名为chromedriverWindows下是chromedriver.exe的可执行文件。将这个文件放在一个你记得住的目录比如D:\WebDriver\。然后将这个目录的路径添加到系统的环境变量PATH中。这是关键一步能让Python在任何位置都能找到这个驱动。第三步验证安装创建一个最简单的Python脚本test_selenium.pyfrom selenium import webdriver from selenium.webdriver.common.by import By import time # 如果驱动已在PATH中这行代码会自动找到并启动Chrome driver webdriver.Chrome() try: driver.get(https://www.baidu.com) print(f页面标题是{driver.title}) time.sleep(2) # 等待2秒看看页面效果 finally: driver.quit() # 务必关闭浏览器释放资源运行这个脚本。如果弹出了一个Chrome浏览器窗口并打开了百度且控制台打印出了标题那么恭喜你环境配置成功了实操心得我强烈推荐将驱动放在固定目录并配置PATH而不是每次在代码里指定路径。这样代码更简洁webdriver.Chrome()也便于团队协作和持续集成环境配置。如果遇到WebDriverException十有八九是驱动版本不匹配或PATH没配好回头检查这两点。3. 从零到一你的第一个Selenium自动化脚本环境搞定我们来点实际的。假设我们要自动化完成一个经典操作在百度搜索“Selenium”并打开第一个结果。3.1 脚本编写与逐行解析from selenium import webdriver 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 import time # 1. 初始化驱动 driver webdriver.Chrome() # 可选设置浏览器窗口最大化避免元素被遮挡 driver.maximize_window() wait WebDriverWait(driver, 10) # 创建一个最多等待10秒的等待对象 try: # 2. 导航到目标页面 driver.get(https://www.baidu.com) print(f已访问{driver.current_url}) # 3. 定位搜索框并输入关键词 # 通过检查网页元素发现百度搜索框的id是‘kw’ search_box driver.find_element(By.ID, kw) search_box.clear() # 清空可能存在的默认文本 search_box.send_keys(Selenium自动化测试) # 输入文本 search_box.send_keys(Keys.RETURN) # 模拟键盘回车键进行搜索 # 也可以使用 search_box.submit()如果该元素在form表单内 # 4. 等待搜索结果加载完成并定位第一个结果链接 # 等待直到搜索结果区域的某个标志性元素出现这里用第一个结果的标题链接通过CSS选择器 first_result_locator (By.CSS_SELECTOR, #content_left .result h3 a) first_link wait.until(EC.presence_of_element_located(first_result_locator)) # 5. 获取链接文本和URL并点击 print(f第一个结果的标题是{first_link.text}) print(f链接地址是{first_link.get_attribute(href)}) # 点击前记录当前窗口句柄可以理解为浏览器标签页的ID original_window driver.current_window_handle first_link.click() # 点击链接可能会在新标签页打开 # 6. 切换到新打开的窗口 # 等待新窗口出现窗口数量变为2 wait.until(EC.number_of_windows_to_be(2)) # 获取所有窗口句柄并切换到不是原始窗口的那个 all_windows driver.window_handles for window in all_windows: if window ! original_window: driver.switch_to.window(window) break # 7. 在新窗口中进行操作例如等待页面加载并打印新标题 time.sleep(3) # 简单等待页面加载实际项目应用更智能的等待见下文 print(f新页面的标题是{driver.title}) except Exception as e: print(f程序运行出错{e}) # 可以在这里截图方便排查 driver.save_screenshot(error_screenshot.png) finally: # 8. 等待几秒后关闭所有浏览器窗口 time.sleep(5) driver.quit()3.2 核心操作原理解析这个脚本虽然不长但涵盖了Selenium最核心的几个操作driver.get(url)这是导航命令让浏览器加载指定URL。它会一直等待页面load事件触发即整个HTML文档加载完毕但对于AJAX或JS动态加载的内容load事件触发时可能并未完全加载这就是为什么我们需要“等待”。元素定位driver.find_element(By.ID, “kw”)。这是Selenium的基石。By.ID是定位策略”kw”是定位器的值。Selenium提供了8种主要的定位方式ID, NAME, CLASS_NAME, TAG_NAME, LINK_TEXT, PARTIAL_LINK_TEXT, CSS_SELECTOR, XPATH我们后面会详细对比。元素交互send_keys()用于输入文本或模拟按键click()用于点击。这些方法会尝试在元素上执行对应的用户操作。如果元素不可见、被遮挡或不可交互则会抛出ElementNotInteractableException。窗口管理current_window_handle和window_handles用于获取窗口标识。switch_to.window(handle)用于在多个标签页或窗口间切换。这是处理弹窗或点击链接开新页面的关键。等待脚本中使用了两种等待。time.sleep(3)是强制固定等待不推荐在生产脚本中使用因为它效率低下且不可靠网络或机器慢时可能不够。我们使用的是WebDriverWait配合expected_conditionsEC的显式等待它会在指定时间内10秒轮询条件一旦条件满足如元素出现就立即继续否则超时抛出异常。这是编写健壮自动化脚本的黄金法则。4. 元素定位八仙过海各显神通定位不到元素是Selenium新手遇到的最多问题。网页上的按钮、输入框、链接在Selenium眼里都是“元素”。你要操作它首先得在代码里“找到”它。Selenium提供了8种定位器就像8种不同的“寻人启事”。4.1 八大定位策略深度对比定位方式示例代码优点缺点与注意事项By.IDfind_element(By.ID, “loginBtn”)最快、最优先选择。ID在HTML中应唯一定位精准高效。并非所有元素都有ID动态生成的ID含随机后缀不可用。By.NAMEfind_element(By.NAME, “username”)常用于表单元素input, select语义化较好。Name属性也可能不唯一需确保在当前上下文唯一。By.CLASS_NAMEfind_element(By.CLASS_NAME, “btn-primary”)适用于通过CSS类批量定位元素。一个元素可能有多个类空格分隔需匹配全部类名常因样式调整而改变。By.TAG_NAMEfind_element(By.TAG_NAME, “a”)用于定位特定类型的标签如所有链接a、所有输入框input。通常返回多个元素需用find_elements并索引或结合其他定位器。By.LINK_TEXTfind_element(By.LINK_TEXT, “忘记密码”)精准定位超链接的完整可见文本。文本必须完全匹配包括空格和大小写对动态文本或局部文本无效。By.PARTIAL_LINK_TEXTfind_element(By.PARTIAL_LINK_TEXT, “忘记”)定位超链接的部分可见文本更灵活。可能匹配到多个链接需注意唯一性。By.CSS_SELECTORfind_element(By.CSS_SELECTOR, “#content .list li:nth-child(1) a”)功能强大、灵活、速度快。支持所有CSS选择器语法可组合复杂条件。语法需要学习复杂选择器可读性稍差。现代前端框架下首选。By.XPATHfind_element(By.XPATH, “//div[id‘content’]//a[contains(text(), ‘下载’)]”)功能最强大。可以基于元素在DOM树中的任何层级、属性、文本进行定位。语法相对复杂执行速度通常比CSS选择器慢过度依赖绝对路径以/开头非常脆弱。4.2 如何选择与高效定位实战原则一优先级从高到低ID CSS Selector XPath 其他。有唯一ID直接用ID。没有ID优先考虑CSS选择器因为它性能好且被浏览器原生支持。XPath是最后的“万能钥匙”尤其是需要根据文本内容定位时。原则二避免绝对路径和索引依赖。像/html/body/div[3]/div[2]/div[1]/a这样的XPath绝对路径页面结构稍有变动比如中间加了个div就会失效。同样find_elements(By.CLASS_NAME, “item”)[0]依赖固定索引也很脆弱。原则三利用浏览器开发者工具。这是你定位元素的“眼睛”。在Chrome中右键点击页面元素 - “检查”在Elements面板中右键该元素 - “Copy” - 你可以直接复制到“Copy selector”CSS选择器或“Copy XPath”。这是一个很好的起点但不要完全依赖复制的选择器它们可能又长又脆弱需要你根据页面结构进行简化和优化。实战定位一个复杂的动态表格行假设有一个用户列表表格每行有一个“删除”按钮但按钮没有唯一ID。我们需要定位到用户“张三”所在行的删除按钮。from selenium.webdriver.common.by import By # 不推荐依赖不稳定的索引 # delete_btn driver.find_elements(By.CLASS_NAME, “delete-btn”)[2] # 假设张三在第三行 # 推荐方案1使用XPath结合文本和层级关系 # 思路先找到包含文本“张三”的单元格td再找到其同行tr下的按钮 delete_btn_xpath “//td[contains(text(), ‘张三’)]/..//button[contains(class, ‘delete-btn’)]” delete_btn driver.find_element(By.XPATH, delete_btn_xpath) # 推荐方案2使用CSS Selector结合属性选择器如果按钮有特定的data属性如data-username # 假设HTML为button class“delete-btn”>from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待元素出现在DOM中不一定可见 element_present EC.presence_of_element_located((By.ID, “dynamic-element”)) # 等待元素在页面上可见且可交互长宽都大于0 element_visible EC.visibility_of_element_located((By.CSS_SELECTOR, “.modal”)) # 等待元素可以被点击可见且启用 element_clickable EC.element_to_be_clickable((By.ID, “submit-btn”)) # 等待元素包含特定的文本 text_present EC.text_to_be_present_in_element((By.CLASS_NAME, “status”), “完成”) # 等待页面标题包含特定文字 title_contains EC.title_contains(“订单详情”) # 等待某个元素从DOM中消失或不可见 element_invisible EC.invisibility_of_element_located((By.ID, “loading-spinner”)) # 等待新窗口打开 new_window_opened EC.number_of_windows_to_be(2) # 等待Alert弹窗出现 alert_present EC.alert_is_present() # 使用示例 wait WebDriverWait(driver, 10, poll_frequency0.5, ignored_exceptions[NoSuchElementException]) submit_button wait.until(EC.element_to_be_clickable((By.ID, “submit-btn”))) submit_button.click() # 处理Alert弹窗 alert wait.until(EC.alert_is_present()) print(alert.text) # 获取弹窗文本 alert.accept() # 点击“确定” # alert.dismiss() # 点击“取消”5.3 自定义等待条件当内置条件不满足需求时你可以自定义一个函数只要这个函数返回True表示条件满足或非False值如一个WebElement对象等待就会成功结束。def element_has_stopped_moving(driver): “”“自定义条件等待一个具有动画效果的元素位置稳定下来。”“” element driver.find_element(By.ID, “floating-widget”) location1 element.location time.sleep(0.1) # 等待一小段时间 location2 element.location # 如果两次获取的位置相同说明元素已静止 return location1 location2 # 使用自定义条件 wait.until(element_has_stopped_moving)避坑指南等待超时异常TimeoutException是自动化脚本中最常见的异常之一。排查时首先手动操作一遍流程确认元素定位器是否正确。其次考虑增加等待时间或者检查等待的条件是否合适例如元素在DOM中但被CSS隐藏了presence_of_element_located会成功但visibility_of_element_located会失败。在关键步骤前后使用driver.save_screenshot(‘step1.png’)截图能极大帮助离线分析问题。6. 高级交互与复杂场景处理掌握了定位和等待你已经能完成80%的自动化操作。剩下的20%是更复杂的交互和特殊场景处理好了才能让脚本真正健壮。6.1 处理下拉选择框HTML原生的select标签Selenium提供了专门的Select类来处理这比模拟点击选项要稳定得多。from selenium.webdriver.support.ui import Select # 定位到select元素 country_select_element driver.find_element(By.NAME, “country”) # 创建Select对象 country_select Select(country_select_element) # 三种选择方式 country_select.select_by_visible_text(“中国”) # 通过选项的可见文本 country_select.select_by_value(“CN”) # 通过选项的value属性 country_select.select_by_index(1) # 通过索引从0开始 # 获取所有选项 all_options country_select.options for option in all_options: print(option.text, option.get_attribute(“value”)) # 获取当前选中的选项 selected_option country_select.first_selected_option print(f”当前选中{selected_option.text}”)6.2 执行JavaScript代码有些操作通过WebDriver API难以直接完成比如滚动页面、修改元素属性、执行复杂的DOM操作。这时可以直接注入并执行JavaScript。# 滚动到页面底部 driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) time.sleep(2) # 滚动到某个元素位置 element driver.find_element(By.ID, “footer”) driver.execute_script(“arguments[0].scrollIntoView(true);”, element) # true表示与顶部对齐 # 修改元素样式例如高亮显示 driver.execute_script(“arguments[0].style.border ‘3px solid red’;”, element) # 获取页面性能数据Navigation Timing API load_time driver.execute_script( “return performance.timing.loadEventEnd - performance.timing.navigationStart;” ) print(f”页面加载耗时{load_time}ms”) # 处理那些被遮挡或常规click()无效的元素 # 有时元素被其他层如div覆盖直接click()会报错可以用JS点击 driver.execute_script(“arguments[0].click();”, element)注意虽然execute_script很强大但应作为最后手段。因为它绕过了WebDriver的模拟用户操作机制可能引发一些不可预知的状态问题且不利于脚本的可读性和可维护性。6.3 处理文件上传文件上传的input type”file”元素直接使用send_keys()传入本地文件的绝对路径即可无需模拟点击“选择文件”按钮。# 定位文件上传输入框 upload_input driver.find_element(By.CSS_SELECTOR, “input[type‘file’]”) # 发送文件绝对路径 file_path os.path.abspath(“./test_data/avatar.jpg”) upload_input.send_keys(file_path) # 之后通常需要点击“上传”或“提交”按钮关键点确保你发送的是文件的完整、绝对路径。相对路径可能导致找不到文件。另外有些网站的文件上传是通过JavaScript或Flash实现的复杂组件而不是原生的input type”file”这种情况就需要更复杂的处理比如使用pyautogui库模拟键盘操作不跨平台或者与开发沟通是否可以提供测试专用的上传接口。6.4 操作Cookie与本地存储自动化登录后保存Cookie可以避免下次再走登录流程这在爬虫和测试中很有用。# 1. 登录后获取所有Cookie cookies driver.get_cookies() print(cookies) # Cookie是一个字典列表 # 可以将cookies保存到文件如json供后续使用 import json with open(‘cookies.json’, ‘w’) as f: json.dump(cookies, f) # 2. 再次启动浏览器后加载Cookie实现免登录 driver.get(“https://target-site.com/login”) # 先访问域名以设置Cookie time.sleep(2) # 确保页面加载建立会话 with open(‘cookies.json’, ‘r’) as f: cookies json.load(f) for cookie in cookies: # 添加Cookie前可能需要删除‘expiry’字段因为它可能是浮点数 if ‘expiry’ in cookie: # 有时需要将过期时间转换为整数 cookie[‘expiry’] int(cookie[‘expiry’]) try: driver.add_cookie(cookie) except Exception as e: print(f”添加Cookie失败{cookie[‘name’]}, 错误{e}”) # 刷新页面或跳转到需要登录的页面此时应该已处于登录状态 driver.refresh() # 或者 driver.get(“https://target-site.com/user/profile”)注意事项Cookie通常与域名、路径绑定。添加Cookie前你必须先让浏览器访问该Cookie所属的域名哪怕只是首页否则会报错InvalidCookieDomainException。此外会话Cookie没有expiry字段在浏览器关闭后失效持久化Cookie则可以保存到文件。7. 实战构建一个简单的页面对象模型框架当你的自动化脚本越来越长维护就会成为噩梦。元素定位器散落在各个测试步骤中页面结构一变你要改几十处代码。这时你需要页面对象模型。POM的核心思想是将页面抽象成一个类页面上的元素定义为类的属性页面上的操作定义为类的方法。这样测试脚本只关心业务流程不关心页面细节。7.1 基础POM实现我们以一个简单的登录页面为例。pages/login_page.pyfrom selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: “”“登录页面的页面对象类”“” # 页面元素定位器Locators USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.ID, “password”) LOGIN_BUTTON (By.CSS_SELECTOR, “button[type‘submit’]”) ERROR_MESSAGE (By.CLASS_NAME, “alert-error”) def __init__(self, driver): self.driver driver self.wait WebDriverWait(self.driver, 10) def load(self, url): “”“导航到登录页面”“” self.driver.get(url) # 可以在这里添加一个等待确保页面关键元素加载完成 self.wait.until(EC.presence_of_element_located(self.USERNAME_INPUT)) return self # 支持链式调用 def enter_username(self, username): “”“输入用户名”“” user_elem self.wait.until(EC.element_to_be_clickable(self.USERNAME_INPUT)) user_elem.clear() user_elem.send_keys(username) return self def enter_password(self, password): “”“输入密码”“” pwd_elem self.wait.until(EC.element_to_be_clickable(self.PASSWORD_INPUT)) pwd_elem.clear() pwd_elem.send_keys(password) return self def click_login(self): “”“点击登录按钮”“” login_elem self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON)) login_elem.click() # 点击后页面可能会跳转返回下一个页面的Page Object如HomePage # 这里我们先返回自身实际项目中根据情况返回新页面的对象 return self def get_error_message(self): “”“获取错误提示信息登录失败时”“” try: error_elem self.wait.until(EC.visibility_of_element_located(self.ERROR_MESSAGE)) return error_elem.text except: return None # 如果没有错误信息返回None def login(self, username, password): “”“登录流程的快捷方法”“” self.enter_username(username) self.enter_password(password) self.click_login()tests/test_login.pyimport pytest from selenium import webdriver from pages.login_page import LoginPage class TestLogin: def setup_method(self): self.driver webdriver.Chrome() self.driver.implicitly_wait(0) # 禁用隐式等待 self.login_page LoginPage(self.driver) def teardown_method(self): self.driver.quit() def test_login_success(self): “”“测试成功登录”“” self.login_page.load(“https://example.com/login”) # 链式调用使代码更清晰 self.login_page.enter_username(“valid_user”).enter_password(“valid_pass”).click_login() # 断言验证是否跳转到首页假设首页标题包含“Dashboard” WebDriverWait(self.driver, 10).until(EC.title_contains(“Dashboard”)) assert “Dashboard” in self.driver.title def test_login_failure(self): “”“测试失败登录”“” self.login_page.load(“https://example.com/login”) self.login_page.login(“invalid_user”, “wrong_pass”) error_msg self.login_page.get_error_message() assert error_msg is not None assert “用户名或密码错误” in error_msg7.2 POM的优势与进阶技巧优势高可维护性页面元素定位器集中管理页面结构一变只需修改对应的Page Class。高可读性测试脚本读起来像自然语言login_page.enter_username(“xxx”)业务逻辑清晰。低冗余公共操作封装成方法避免代码重复。便于协作页面对象和测试用例可以分给不同的人编写。进阶技巧页面工厂模式可以创建一个PageFactory自动初始化页面对象并处理页面跳转时对象的传递。复合组件对于页面中重复出现的复杂组件如导航栏、侧边栏、模态框可以单独抽象成Component类然后在Page类中引用。等待策略封装在Page类的基类中统一封装等待逻辑比如重写find_element方法使其自动包含显式等待。实操心得不要过度设计。对于小型项目或一次性脚本简单的线性脚本可能更高效。POM的引入会带来一定的架构复杂度。只有当你的自动化项目需要长期维护、页面较多、团队协作时POM的价值才会完全体现。初期可以从一个核心页面开始尝试逐步重构。8. 常见疑难杂症与性能优化即使掌握了所有API在实际项目中你还是会碰到各种“妖魔鬼怪”。这里记录了一些我踩过的坑和解决方案。8.1 元素定位常见问题排查表问题现象可能原因排查步骤与解决方案NoSuchElementException1. 定位器写错了。2. 元素在iframe/frame内。3. 元素是动态加载的还没出现。4. 页面有多个匹配元素find_element只返回第一个。1. 用浏览器开发者工具复查定位器。2. 使用driver.switch_to.frame()切换到正确的frame。3. 增加显式等待等待元素出现/可见。4. 使用find_elements检查匹配数量优化定位器使其唯一。ElementNotInteractableException1. 元素不可见被隐藏或CSS设为display:none。2. 元素被其他元素如遮罩层遮挡。3. 元素是disabled状态。1. 等待元素可见EC.visibility_of。2. 滚动元素到视口scrollIntoView。3. 检查并移除遮挡物可能需要JS。4. 检查元素disabled属性。StaleElementReferenceException你持有的元素对象所对应的DOM元素已经失效页面刷新、AJAX更新导致元素被重新渲染。根本解决每次操作前重新查找元素。临时解决在WebDriverWait.until中捕获此异常并重试。TimeoutException显式等待的条件在指定时间内未满足。1. 增加等待时间。2. 检查等待条件是否合理如等“可见”但元素一直隐藏。3. 检查网络或服务器是否异常。4. 在超时前截图分析页面状态。点击/输入没反应1. 点错了元素如点到不可见的父容器。2. 页面有未处理的JavaScript错误或阻塞。3. 需要触发特定事件如onchange。1. 确保点击的是正确的、可交互的子元素。2. 查看浏览器控制台F12 - Console是否有JS报错。3. 尝试用send_keys(Keys.TAB)触发失焦事件或用JS直接设置值并触发事件。8.2 性能优化与最佳实践使用无头模式如果不需要观察浏览器界面使用无头模式可以大幅节省资源特别适合在服务器或CI/CD环境中运行。from selenium.webdriver.chrome.options import Options chrome_options Options() chrome_options.add_argument(“--headless”) # 启用无头模式 chrome_options.add_argument(“--disable-gpu”) # 某些系统需要 chrome_options.add_argument(“--no-sandbox”) # Linux环境有时需要 driver webdriver.Chrome(optionschrome_options)复用浏览器会话对于需要多次执行的测试或爬虫可以考虑复用同一个driver实例而不是每次quit()再重新启动。启动浏览器是开销最大的操作。优化等待策略将全局的隐式等待设置为0完全使用显式等待。为不同的操作设置合理的超时时间如登录按钮10秒加载大数据表格30秒。适当调整poll_frequency轮询频率默认0.5秒在等待缓慢操作时可以适当调大如1秒减少CPU占用。并行执行对于大量独立的测试用例可以使用pytest-xdist或unittest的TestSuite配合多进程/线程来并行运行。关键每个线程/进程必须使用自己独立的driver实例绝对不要共享。资源清理务必在finally块或测试框架的teardown方法中调用driver.quit()而不是driver.close()。quit()会关闭整个浏览器和WebDriver进程释放资源close()只关闭当前标签页。处理弹窗和通知在启动浏览器时添加选项阻止一些弹窗或保存密码提示。chrome_options.add_experimental_option(“prefs”, { “profile.default_content_setting_values.notifications”: 2, # 禁用通知 “credentials_enable_service”: False, # 禁用保存密码提示 “profile.password_manager_enabled”: False })8.3 与爬虫框架的配合Selenium虽然强大但速度慢、资源消耗大。在爬虫项目中通常的策略是“能不用Selenium就不用”。优先使用requests/aiohttpBeautifulSoup/parsel解析静态HTML。只有当目标网站核心数据由JavaScript动态渲染如单页面应用SPA。有复杂的反爬机制如验证码、行为指纹需要模拟真人操作。操作流程复杂涉及多次交互如登录、翻页、筛选。在这些情况下才引入Selenium。一个常见的混合架构是用Selenium完成登录、获取Cookie、触发JS渲染然后将最终的HTML源码或关键的API请求参数提取出来交给更高效的爬虫框架进行后续的数据提取和并发请求。这样既解决了动态渲染问题又保证了整体效率。