01
Selenium 是什么:浏览器自动化的事实标准
Selenium 是驱动真实浏览器的自动化框架,也是 W3C WebDriver 协议的参考实现。它启动一个货真价实的 Chrome/Firefox/Edge——能执行 JavaScript、渲染 CSS、弹 Cookie——然后替你点击、输入、滚动、截图。两个典型用途:UI 自动化测试(回归测试的主力)和网页抓取(对付需要执行 JS 才能出内容的页面)。
它比 requests 慢得多,但看到的就是用户看到的。什么时候用它:目标页面靠 JS 渲染、需要登录交互、或者你写的本来就是测试。什么时候不用:静态页面用 requests + 解析库就够,快一个数量级。
两句话记住它的位置:测试眼里它是回归测试的执行器——每次发版前把关键流程点一遍;数据眼里它是「能交互的抓取器」——滚动加载、点击展开、登录后才可见的内容它都能拿。代价是快不起来,所以能不用则不用。
- 定位
- W3C WebDriver 协议参考实现,浏览器自动化事实标准
- 当前版本
- 4.52(2026-11 查 PyPI,主线 4.x)
- Python 要求
- ≥ 3.10
- 协议
- Apache 2.0
- 驱动管理
- Selenium Manager 内置(4.6+),不再手配 chromedriver
- 支持浏览器
- Chrome / Firefox / Edge / Safari
- 语言绑定
- Python / Java / C# / Ruby / JavaScript
- 老教程写法
- find_element_by_* 已在 4.3 移除,勿用
webdriver.Chrome(),它自动检测/下载匹配的驱动(必要时连浏览器都能帮你装)。2022 年之后的教程如果还教你「先去官网下 chromedriver」,直接跳过那段。02
安装:一个包 + 一个本机浏览器
Selenium 本体很小,真正的「环境」是你的浏览器。本机装有 Chrome、Firefox、Edge 任意一个即可,驱动交给 Selenium Manager。
# 装包,一行搞定;建议先建虚拟环境 pip install selenium # 不需要装 webdriver-manager!4.6+ 的 Selenium Manager 已内置接管
# 确认本机有浏览器(macOS 示例) /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version # Linux 服务器一般没有浏览器,装一个: # Debian/Ubuntu: apt install chromium-browser 或 google-chrome-stable
# verify.py —— 能打印标题并退出,就是全通了 from selenium import webdriver driver = webdriver.Chrome() # 第一次跑会自动下载驱动,稍等 driver.get("https://example.com") print(driver.title) # Example Domain driver.quit()
03
快速上手:十行代码自动搜索一次
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys driver = webdriver.Chrome() # Selenium Manager 自动管驱动 driver.get("https://www.bing.com") box = driver.find_element(By.NAME, "q") # 定位搜索框(name 属性是 q) box.send_keys("Selenium WebDriver", Keys.RETURN) # 输入并回车 print(driver.title) # 现在已是结果页的标题 driver.save_screenshot("result.png") # 截一张图存档 driver.quit() # 关浏览器、结束会话
-
01
启动会话
webdriver.Chrome()返回 driver 对象,它是你接下来所有操作的入口。Firefox/Edge 换成webdriver.Firefox()/webdriver.Edge(),用法完全一样。 -
02
打开页面
driver.get(url)会等页面基本加载完成才返回(与 requests 不同,它真的渲染 JS)。 -
03
定位 + 操作
所有交互都是两步:find_element 找到元素,再 click() / send_keys()。Keys.RETURN 模拟回车,Keys.CONTROL + "a" 这类组合键也在 Keys 里。
-
04
断言 + 收尾
测试的本质是断言:title 对不对、元素在不在、文本有没有。最后一定 quit()——不 quit 会留下一堆残留的 chrome 进程。
04
核心概念:定位、等待、页面操作与无头模式
WebDriver 与 Selenium Manager:手配驱动已成历史
3.x 时代的入门流程是「查浏览器版本 → 官网下载对应 chromedriver → 放进 PATH → 版本不匹配再重来」。Selenium 4.6 把这套流程收进 Selenium Manager:一个用 Rust 写的内置工具,绑定启动时自动检测浏览器、下载/缓存匹配的驱动。你要做的只有 webdriver.Chrome()。只有在驱动下载不通(内网)或要固定驱动版本时才需要人工介入。
八大定位器:find_element 与 find_elements
定位是 Selenium 的基本功。统一入口是 find_element(By.X, "值")——注意 4.3 起老写法 find_element_by_id() 已彻底删除。单数版返回第一个匹配(找不到抛 NoSuchElementException),复数版 find_elements 返回列表(找不到给空列表,不报错)。优先级经验:CSS_SELECTOR 为主,XPATH 兜底——CSS 快且直观,XPATH 能向上找父节点、按文本定位。
driver.find_element(By.ID, "username") driver.find_element(By.CSS_SELECTOR, "#login input[name='password']") driver.find_element(By.XPATH, "//button[contains(text(), '登录')]") items = driver.find_elements(By.CSS_SELECTOR, ".search-result li") print(len(items)) # find_elements 找不到返回 [],适合计数与遍历
等待机制:不懂这个必踩坑
页面是异步的:你 get 之后,内容可能还没渲染出来,find_element 立刻就找不到——九成的「No such element」都是这个。time.sleep 硬等是下策:等短了还是炸,等长了白浪费。正确姿势是显式等待:给一个最长期限,条件一满足立刻往下走。
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 最多等 10 秒,元素「可见且可点」就立刻返回 btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#submit")) ) btn.click() # 隐式等待是另一条路:全局设置一次,所有 find_element 自动轮询 driver.implicitly_wait(5) # 注意:隐式与显式不要混用,会造成难以理解的叠加等待
页面操作、无头模式与 Options
常用操作三板斧:点击 click()、输入 send_keys()、清空 clear();下拉框要包一层 Select;多窗口和 iframe 用 switch_to 切换;特殊操作(滚动、改属性)直接执行 JS。跑在服务器上没有显示器?headless 无头模式让浏览器在内存里渲染,过程一样、不弹窗口。
options = webdriver.ChromeOptions() options.add_argument("--headless=new") # 新版无头模式 options.add_argument("--window-size=1920,1080") # 无头下也要给尺寸 options.add_argument("--disable-gpu") driver = webdriver.Chrome(options=options) # 切换窗口 / iframe / 执行 JS 的常用姿势 driver.switch_to.window(driver.window_handles[-1]) # 切到新开的窗口 driver.switch_to.frame("login-iframe") # 进 iframe driver.switch_to.default_content() # 出来,回主文档 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
05
速查表:定位、等待与常用操作
下面这块表格能搜。忘了某个定位器或条件怎么写,直接搜 点击、等待、iframe 之类的词。
| 定位器 | 说明 |
|---|---|
| By.ID | 按 id 属性定位,页内唯一,优先用 |
| By.NAME | 按 name 属性,常见于表单控件 |
| By.CSS_SELECTOR | CSS 选择器,如 "#main .item a";首选方案 |
| By.XPATH | XML 路径,如 //input[@name='q'];能向上找、按文本找 |
| By.CLASS_NAME | 单个 class 名(不能写多个空格分隔) |
| By.TAG_NAME | 按标签名,如 button、tr |
| By.LINK_TEXT | 链接的完整文字精确匹配 |
| By.PARTIAL_LINK_TEXT | 链接文字的部分匹配 |
| find_element(By.X, "v") | 返回第一个匹配;找不到抛 NoSuchElementException |
| find_elements(By.X, "v") | 返回列表;找不到是空列表,不报错 |
| el.find_element(By.X, "v") | 在元素内部继续找,缩小范围 |
| locate_with(By.TAG_NAME, "input").below(el) | Selenium 4 相对定位器:按方位找(above/below/near) |
| 顺序建议 | 先 ID/NAME,再 CSS_SELECTOR,动态结构才上 XPATH |
| expected_conditions | 等什么 |
|---|---|
| presence_of_element_located | 元素已在 DOM(可能还不可见) |
| visibility_of_element_located | 元素可见:有尺寸且非 display:none |
| element_to_be_clickable | 可见且可点击,点按钮前用它 |
| presence_of_all_elements_located | 一组元素都出现(结果列表加载完) |
| text_to_be_present_in_element | 元素内出现指定文字 |
| title_is / title_contains | 标题等于 / 包含某文本 |
| invisibility_of_element_located | 元素消失,等 loading 遮罩关闭 |
| staleness_of(el) | 元素已过期离开 DOM(确认页面刷新) |
| new_window_is_opened(n) | 打开了新窗口 |
| alert_is_present | JS 弹窗出现,配 driver.switch_to.alert |
| url_changes("…") | URL 发生变化(提交表单后等跳转) |
| element_selection_state_to_be | 复选框/单选的选中状态符合预期 |
| WebDriverWait(driver, 10).until(EC.…) | 标准组合:超时抛 TimeoutException |
| driver.implicitly_wait(5) | 隐式等待:全局兜底,别与显式等待混用 |
| 操作 | 说明 |
|---|---|
| driver.get(url) | 打开页面,等待基本加载完成 |
| el.click() / el.send_keys("…") | 点击 / 输入;send_keys 也接收 Keys 组合键 |
| el.clear() | 清空输入框,重填前先调 |
| el.text / el.get_attribute("href") | 读可见文本 / 读属性值 |
| Select(el).select_by_visible_text("…") | 下拉框操作(from selenium.webdriver.support.ui import Select) |
| driver.switch_to.window(driver.window_handles[-1]) | 切到最新窗口,句柄顺序即打开顺序 |
| driver.switch_to.frame("id") / default_content() | 进入 iframe / 回主文档 |
| driver.execute_script("js代码") | 执行 JavaScript:滚动、改样式都行 |
| driver.save_screenshot("a.png") | 整页截图;el.screenshot("b.png") 截单个元素 |
| driver.back() / refresh() / current_url | 后退 / 刷新 / 取当前地址 |
| driver.get_cookies() / delete_all_cookies() | Cookie 读写,登录态处理常用 |
| driver.quit() | 关浏览器并结束会话,务必放 finally |
| el.is_displayed() / is_enabled() | 断言元素可见/可用,写测试常配对使用 |
| from selenium.webdriver.common.keys import Keys | 组合键:Keys.RETURN、Keys.CONTROL + "a" |
| Options 配置 | 说明 |
|---|---|
| webdriver.ChromeOptions() | 创建配置对象,启动时传入 webdriver.Chrome(options=o) |
| --headless=new | 无头模式:不弹窗口,服务器/CI 必备 |
| --window-size=1920,1080 | 窗口尺寸;无头模式也建议显式设置 |
| --disable-gpu | 禁 GPU 加速,兼容性兜底 |
| --no-sandbox | Docker / root 环境常配,否则可能启动失败 |
| --start-maximized | 窗口最大化,可见元素更全 |
| user-agent=Mozilla/… | 自定义 UA 字符串 |
| --user-data-dir=/path | 使用已有用户配置目录(带登录态启动) |
| options.page_load_strategy = "eager" | 页面加载策略:eager 不等全部资源 |
| driver.set_page_load_timeout(30) | 页面加载超时,防卡死 |
| driver.implicitly_wait(5) | 全局隐式等待(放 Options 表是因为常一起配) |
| --proxy-server=http://127.0.0.1:7890 | 给浏览器配代理 |
| --incognito | 无痕模式启动,不带历史 Cookie |
没有匹配的用法。换个词试试,比如「等待」「截图」「无头」。
06
实战:搜索自动化完整脚本
把概念串成一个能跑的测试脚本:打开搜索引擎 → 等输入框可点 → 输入关键词回车 → 显式等结果列表 → 断言结果非空 → 截图存档。约 35 行,本机装有 Chrome 即可跑:
# 运行前:pip install selenium(本机装有 Chrome,驱动自动管理) from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC KEYWORD = "Selenium WebDriver" def main(): options = webdriver.ChromeOptions() options.add_argument("--window-size=1280,900") driver = webdriver.Chrome(options=options) try: driver.get("https://www.bing.com") # 1. 等搜索框「可点」再输入,不用 time.sleep 硬等 box = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.NAME, "q")) ) box.send_keys(KEYWORD + "\n") # 回车即搜索 # 2. 显式等待结果列表出现(页面异步渲染,必须等) results = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located( (By.CSS_SELECTOR, "#b_results > li") ) ) # 3. 断言:这就是自动化测试与裸跑的区别 assert len(results) > 0, "结果列表为空,搜索可能失败" first = results[0].text.replace("\n", " ") print(f"共 {len(results)} 条结果,第一条:{first[:60]}") # 4. 截图存档,方便回看与写报告 driver.save_screenshot("result.png") print("截图已保存 result.png") finally: driver.quit() # 无论成败都关浏览器 if __name__ == "__main__": main()
运行效果
$ python search_test.py 共 12 条结果,第一条:Selenium automates browsers... 截图已保存 result.png
升级一:用 pytest 组织用例(点名)
真实项目不会一个脚本跑到底,而是用 pytest:fixture 负责开关浏览器(每个用例一份全新会话),用例就是一个个断言函数,失败自动记录。上面的脚本拆进去大概是这个样子:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.fixture
def driver():
driver = webdriver.Chrome()
yield driver # 用例在这里运行
driver.quit() # 无论成败都会执行
def test_bing_search(driver):
driver.get("https://www.bing.com")
box = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.NAME, "q"))
)
box.send_keys("Selenium WebDriver\n")
results = WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#b_results > li"))
)
assert len(results) > 0
升级二:Page Object 模式简介
用例一多,「选择器散落在每个测试里」就是灾难——页面一改版要改几十处。Page Object 的解法:每个页面封装成一个类,定位器和操作只写在类里,用例只调用方法。改版时只改 Page 类一处:
# pages.py —— 页面长什么样,只有这个文件知道 from selenium.webdriver.common.by import By class SearchPage: BOX = (By.NAME, "q") RESULTS = (By.CSS_SELECTOR, "#b_results > li") def __init__(self, driver): self.driver = driver def search(self, keyword): self.driver.find_element(*self.BOX).send_keys(keyword + "\n") def result_count(self) -> int: return len(self.driver.find_elements(*self.RESULTS)) # test_search.py —— 用例里只出现业务语言,不出现选择器 def test_bing_search(driver): page = SearchPage(driver) driver.get("https://www.bing.com") page.search("Selenium WebDriver") assert page.result_count() > 0
07
避坑:四个最常见的坑
坑一:照老教程写 find_element_by_id,报 AttributeError
Selenium 4.3 起,find_element_by_* / find_elements_by_* 系列方法全部移除,2022 年前的教程和 Stack Overflow 高票答案里到处都是。统一改成新写法:
# 旧(4.3 起已删除,直接报错) driver.find_element_by_id("username") # 新(唯一写法) from selenium.webdriver.common.by import By driver.find_element(By.ID, "username")
坑二:NoSuchElementException——九成是没等待
页面 JS 渲染需要时间,get 返回 ≠ 内容就绪。解决顺序:先用显式等待包住关键定位;等待后仍失败,再人工检查选择器写没写对(id 是不是动态生成的);最后才怀疑页面结构变了。把 time.sleep(3) 全文搜一遍,每处都换成 WebDriverWait。
# 坏:猜时间,猜错了就炸 time.sleep(3) driver.find_element(By.ID, "result") # 好:等条件成立,最快 0.1 秒、最慢 10 秒 el = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, "result")) )
坑三:SessionNotCreatedException / 驱动相关报错
Selenium Manager 需要联网下载驱动:公司内网可能被拦——配代理或手动下载驱动放进 PATH。Docker 里跑还要注意:容器里没有浏览器,要装 chromium 类镜像包,并加 --headless=new --no-sandbox --disable-gpu 三件套。本机报错时,先升级浏览器到最新版再重试,多数情况自愈。
坑四:合规红线——自动化不等于可以乱来
用它做自己系统的测试天经地义;抓第三方站点前,看两样东西:目标站的 robots.txt 与服务条款是否允许。控制频率(加延时、别并发轰炸)、别碰验证码与登录态倒卖这类灰产场景、数据只做个人研究不商用转卖。脚本身份是浏览器,但责任主体是你。
08
进阶方向与资源
- Selenium 官方文档——浏览器、驱动、等待、定位的系统教程都在这。
- Selenium Manager 文档——驱动自动管理的细节与手动配置方案。
- SeleniumHQ/selenium——源码与 issue 区,查报错先例的好地方。
- selenium(PyPI)——版本号与 Python 版本要求以此为准。
- W3C WebDriver 标准——想理解底层协议(会话、命令、响应)再读。
- 官方 Test Practices:Page Object Models——官方推荐的工程化组织方式。
09
收尾:照着勾一遍
0 / 5