Selenium 完全指南
最后更新:2026-03-20 1. Selenium完全指南 · 安装与环境配置(/selenium-wan-quan-zhi-nan/#%E5%AE%89%E8%A3%85%E4%B8%8E%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE) 2. Selenium完全指南 · 启动浏览器(/selenium-wan-quan-zhi-nan/#%E5%90%AF%E5%8A%A8%E6%B5%8F%E8%A7%88%E5%99%A8) 3. Selenium完全指南 · 导航(/selenium-wan-quan-zhi-na
最后更新:2026-03-20
官方文档:https://www.selenium.dev/documentation/
适用版本:Selenium 4.x(2026-05-08 核实)
目录
- Selenium完全指南 · 安装与环境配置
- Selenium完全指南 · 启动浏览器
- Selenium完全指南 · 导航
- Selenium完全指南 · 等待机制
- Selenium完全指南 · 元素定位
- Selenium完全指南 · 读取元素信息
- Selenium完全指南 · 鼠标与键盘操作
- Selenium完全指南 · 表单操作
- Selenium完全指南 · 窗口与标签页
- Selenium完全指南 · 框架与 iframe
- Selenium完全指南 · 弹窗处理
- Selenium完全指南 · JavaScript 执行
- Selenium完全指南 · 截图
- Selenium完全指南 · 最佳实践
- Selenium完全指南 · 常见陷阱与注意事项
安装与环境配置
安装
pip install selenium
Selenium 4.6+ 内置 Selenium Manager,会自动下载匹配当前浏览器版本的 WebDriver,通常不需要手动管理驱动。
如果需要手动管理驱动,也可以使用 webdriver-manager:
pip install webdriver-manager
支持的浏览器
| 浏览器 | 驱动 | 自动管理(Selenium Manager) |
|---|---|---|
| Chrome / Chromium | chromedriver | 支持 |
| Firefox | geckodriver | 支持 |
| Edge | msedgedriver | 支持 |
| Safari | safaridriver(内置) | macOS 自带,需开启开发者模式 |
版本要求
| 包 | 最低版本 | 说明 |
|---|---|---|
| selenium | 4.6+ | 内置 Selenium Manager,推荐 |
| Python | 3.8+ |
启动浏览器
Chrome
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
# 最简启动(Selenium Manager 自动管理驱动)
driver = webdriver.Chrome()
# 带选项启动
options = Options()
options.add_argument("--headless=new") # 无头模式
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
options.add_argument("--user-agent=...") # 自定义 UA
options.add_experimental_option("excludeSwitches", ["enable-automation"]) # 去除自动化标记
driver = webdriver.Chrome(options=options)
# 手动指定驱动路径
service = Service(executable_path="/path/to/chromedriver")
driver = webdriver.Chrome(service=service, options=options)
Chrome Options 常用参数:
| 参数 | 说明 |
|---|---|
--headless=new |
无头模式(Chrome 112+,新版) |
--no-sandbox |
禁用沙箱,Docker 环境必须加 |
--disable-dev-shm-usage |
避免 /dev/shm 空间不足崩溃 |
--window-size=W,H |
设置窗口大小 |
--disable-gpu |
禁用 GPU,旧版无头模式需要 |
--disable-images |
禁止加载图片,提升速度 |
--proxy-server=host:port |
设置代理 |
--ignore-certificate-errors |
忽略 SSL 证书错误 |
--disable-blink-features=AutomationControlled |
隐藏自动化特征 |
Firefox
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument("--headless")
driver = webdriver.Firefox(options=options)
Edge
from selenium import webdriver
driver = webdriver.Edge()
webdriver.Chrome() 参数表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options |
Options | None | 浏览器选项对象 |
service |
Service | None | 驱动服务配置(路径、端口等) |
keep_alive |
bool | True | 保持 HTTP 连接 |
关闭浏览器
driver.close() # 关闭当前标签页
driver.quit() # 关闭整个浏览器进程,释放驱动资源
推荐使用上下文管理器,自动调用 quit():
from selenium import webdriver
with webdriver.Chrome() as driver:
driver.get("https://example.com")
# 退出 with 块时自动 quit()
导航
driver.get(url) # 打开 URL,等待页面加载完成
driver.back() # 后退
driver.forward() # 前进
driver.refresh() # 刷新
driver.get() 参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
str | 必填 | 目标 URL,必须包含协议(https://) |
获取当前页面信息:
driver.current_url # 当前 URL(str)
driver.title # 页面标题(str)
driver.page_source # 页面 HTML 源码(str)
等待机制
Selenium 操作的最大痛点是时序问题。页面元素可能尚未加载完毕,直接操作会抛出异常。
隐式等待(ImplicitWait)
全局设置,对所有 find_element 生效,找不到元素时轮询等待,直到超时。
driver.implicitly_wait(10) # 秒,全局生效
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
time_to_wait |
float | 0 | 最长等待秒数,0 表示不等待 |
缺点:与显式等待混用会产生不可预期的超时叠加,建议只用一种。
显式等待(WebDriverWait)
推荐方式。针对具体条件等待,超时抛出 TimeoutException。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
wait = WebDriverWait(driver, timeout=10)
# 等待元素可见
el = wait.until(EC.visibility_of_element_located((By.ID, "submit")))
# 等待元素可点击
el = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.submit")))
WebDriverWait() 参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
driver |
WebDriver | 必填 | 浏览器驱动实例 |
timeout |
float | 必填 | 最长等待秒数 |
poll_frequency |
float | 0.5 | 轮询间隔秒数 |
ignored_exceptions |
list | None | 等待期间忽略的异常类型列表 |
wait.until() / wait.until_not() 参数表:
| 参数 | 类型 | 说明 |
|---|---|---|
method |
callable | 接受 driver 为参数的可调用对象,返回值为 False/None 时继续等待 |
message |
str | 超时时的错误信息 |
常用 Expected Conditions:
| 条件 | 说明 |
|---|---|
presence_of_element_located(locator) |
元素存在于 DOM(不要求可见) |
visibility_of_element_located(locator) |
元素可见(宽高>0,不被隐藏) |
element_to_be_clickable(locator) |
元素可见且可交互 |
invisibility_of_element_located(locator) |
元素不可见或不在 DOM 中 |
text_to_be_present_in_element(locator, text) |
元素文本包含指定字符串 |
title_contains(title) |
页面标题包含指定字符串 |
title_is(title) |
页面标题完全匹配 |
url_contains(url) |
URL 包含指定字符串 |
alert_is_present() |
弹窗已出现 |
number_of_windows_to_be(num) |
窗口数量达到指定值 |
frame_to_be_available_and_switch_to_it(locator) |
iframe 可用并自动切入 |
staleness_of(element) |
元素已从 DOM 中移除(用于等待页面刷新) |
强制等待(不推荐)
import time
time.sleep(2) # 固定等待,不推荐用于生产代码
元素定位
find_element() / find_elements()
from selenium.webdriver.common.by import By
# 返回单个 WebElement,找不到抛 NoSuchElementException
element = driver.find_element(By.ID, "username")
# 返回 list[WebElement],找不到返回空列表
elements = driver.find_elements(By.CLASS_NAME, "item")
find_element() 参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
by |
By | 必填 | 定位策略,使用 By 类的常量 |
value |
str | 必填 | 选择器值 |
By 定位策略:
| 策略 | 常量 | 示例值 | 说明 |
|---|---|---|---|
| ID | By.ID |
"username" |
匹配 id 属性,最快 |
| Name | By.NAME |
"email" |
匹配 name 属性 |
| Class Name | By.CLASS_NAME |
"btn-primary" |
单个 class 名,不支持复合类 |
| Tag Name | By.TAG_NAME |
"input" |
匹配标签名 |
| CSS Selector | By.CSS_SELECTOR |
"div.card > a" |
标准 CSS 选择器,最灵活 |
| XPath | By.XPATH |
"//div[@class='nav']" |
支持复杂逻辑,但较慢 |
| Link Text | By.LINK_TEXT |
"点击登录" |
匹配 <a> 的完整文本 |
| Partial Link Text | By.PARTIAL_LINK_TEXT |
"登录" |
匹配 <a> 的部分文本 |
CSS 选择器常用语法:
#id /* ID */
.class /* class */
div /* 标签 */
div.card /* 标签 + class */
input[type="text"] /* 属性 */
div > p /* 直接子元素 */
div p /* 后代元素 */
p:nth-child(2) /* 第2个子元素 */
p:first-child /* 第1个子元素 */
XPath 常用语法:
//div[@id='main'] # 属性定位
//a[text()='登录'] # 文本定位
//a[contains(text(),'登录')] # 文本包含
//input[@type='text'] # 属性过滤
//div[@class='nav']//a # 后代
//ul/li[1] # 第1个 li
//li[last()] # 最后一个
//*[contains(@class,'btn')] # class 包含(class 是空格分隔的,@class= 是全量匹配)
//input[@name='q']/.. # 父节点
在元素内部继续查找
WebElement 也有 find_element / find_elements 方法,作用域限定在该元素内:
container = driver.find_element(By.ID, "results")
items = container.find_elements(By.CLASS_NAME, "item")
读取元素信息
element = driver.find_element(By.ID, "title")
element.text # 可见文本内容(str),等价于 innerText
element.get_attribute("href") # 获取 HTML 属性值
element.get_attribute("innerHTML") # 内部 HTML
element.get_attribute("outerHTML") # 包含自身的 HTML
element.get_attribute("value") # 表单元素的当前值(用属性而非 text)
element.get_property("value") # 获取 DOM property(JS 属性,实时值)
element.get_dom_attribute("class") # 只读取 HTML 属性(不读 JS property)
element.is_displayed() # 是否可见(bool)
element.is_enabled() # 是否可交互(bool)
element.is_selected() # 是否被选中,适用于 checkbox/radio(bool)
element.tag_name # 标签名,如 "input"(str)
element.size # 元素大小,如 {"width": 100, "height": 50}
element.location # 相对页面左上角的坐标,如 {"x": 10, "y": 20}
element.rect # 合并 size 和 location,如 {"x":10,"y":20,"width":100,"height":50}
get_attribute() vs get_property() 区别:
| 方法 | 读取来源 | 动态更新 | 典型用途 |
|---|---|---|---|
get_attribute(name) |
HTML 属性 + DOM property | 部分 | 读 href、src、class、value(初始值) |
get_property(name) |
DOM property(JS) | 是 | 读 value(当前用户输入值)、checked |
get_dom_attribute(name) |
仅 HTML 属性 | 否 | 读原始 HTML 中的属性值 |
鼠标与键盘操作
基础点击
element.click() # 左键点击
ActionChains(链式操作)
适用于复杂交互:悬停、双击、右键、拖拽、组合键等。
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
ActionChains 参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
driver |
WebDriver | 必填 | 浏览器驱动实例 |
duration |
int | 250 | 动作持续时间(毫秒),用于模拟人类速度 |
常用鼠标动作:
| 方法 | 参数 | 说明 |
|---|---|---|
click(on_element=None) |
WebElement | None | 左键点击,None 则点当前位置 |
double_click(on_element=None) |
WebElement | None | 双击 |
context_click(on_element=None) |
WebElement | None | 右键点击 |
move_to_element(to_element) |
WebElement | 移动鼠标到元素中心(悬停) |
move_to_element_with_offset(to_element, xoffset, yoffset) |
WebElement, int, int | 移动到元素内偏移位置 |
move_by_offset(xoffset, yoffset) |
int, int | 从当前位置偏移移动 |
click_and_hold(on_element=None) |
WebElement | None | 按住鼠标左键 |
release(on_element=None) |
WebElement | None | 释放鼠标左键 |
drag_and_drop(source, target) |
WebElement, WebElement | 从 source 拖拽到 target |
drag_and_drop_by_offset(source, xoffset, yoffset) |
WebElement, int, int | 拖拽元素偏移指定距离 |
scroll_to_element(element) |
WebElement | 滚动到元素(Selenium 4.2+) |
scroll_by_amount(delta_x, delta_y) |
int, int | 按像素滚动页面 |
perform() |
无 | 执行所有已排队的动作 |
reset_actions() |
无 | 清空已排队的动作 |
使用示例:
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
# 悬停
actions.move_to_element(menu).perform()
# 双击
actions.double_click(element).perform()
# 右键
actions.context_click(element).perform()
# 拖拽到另一个元素
actions.drag_and_drop(source, target).perform()
# 拖拽偏移
actions.drag_and_drop_by_offset(element, xoffset=200, yoffset=0).perform()
# 链式组合
actions.move_to_element(menu).pause(0.5).click(submenu).perform()
# 滚动页面
actions.scroll_by_amount(0, 500).perform() # 向下滚动 500px
# 滚动到元素
actions.scroll_to_element(element).perform()
键盘操作
from selenium.webdriver.common.keys import Keys
element.send_keys("hello world") # 输入文本
element.send_keys(Keys.ENTER) # 按回车
element.send_keys(Keys.TAB) # 按 Tab
element.send_keys(Keys.CONTROL, "a") # Ctrl+A 全选
element.send_keys(Keys.CONTROL, "c") # Ctrl+C 复制
element.send_keys(Keys.CONTROL, "v") # Ctrl+V 粘贴
element.send_keys(Keys.BACKSPACE) # 删除
element.send_keys(Keys.ESCAPE) # Esc
element.send_keys(Keys.PAGE_DOWN) # 向下翻页
element.send_keys(Keys.ARROW_DOWN) # 向下箭头
常用 Keys 常量:
| 常量 | 说明 |
|---|---|
Keys.ENTER |
回车 |
Keys.RETURN |
回车(同上) |
Keys.TAB |
Tab |
Keys.ESCAPE |
Esc |
Keys.SPACE |
空格 |
Keys.BACKSPACE |
退格 |
Keys.DELETE |
Delete |
Keys.CONTROL |
Ctrl |
Keys.ALT |
Alt |
Keys.SHIFT |
Shift |
Keys.COMMAND |
Command(Mac) |
Keys.F5 |
F5(刷新) |
Keys.PAGE_UP |
Page Up |
Keys.PAGE_DOWN |
Page Down |
Keys.HOME |
Home |
Keys.END |
End |
Keys.ARROW_UP/DOWN/LEFT/RIGHT |
方向键 |
表单操作
# 输入框
input_el = driver.find_element(By.NAME, "username")
input_el.clear() # 清空内容
input_el.send_keys("admin") # 输入文本
# 提交表单(在表单内任意元素上调用)
input_el.submit()
# checkbox / radio
checkbox = driver.find_element(By.ID, "agree")
if not checkbox.is_selected():
checkbox.click()
# 下拉框 <select>
from selenium.webdriver.support.ui import Select
select_el = driver.find_element(By.ID, "country")
sel = Select(select_el)
sel.select_by_value("CN") # 按 value 属性选
sel.select_by_visible_text("中国") # 按显示文本选
sel.select_by_index(2) # 按索引选(从0开始)
sel.deselect_all() # 取消所有选中(多选框)
sel.first_selected_option # 当前选中的第一个 WebElement
sel.all_selected_options # 所有选中项的列表
sel.options # 所有选项的列表
窗口与标签页
# 当前窗口句柄
current = driver.current_window_handle # str
# 所有窗口句柄
all_handles = driver.window_handles # list[str]
# 切换到指定窗口
driver.switch_to.window(all_handles[-1])
# 打开新标签页(Selenium 4+)
driver.switch_to.new_window("tab") # 打开新标签页并切换
driver.switch_to.new_window("window") # 打开新窗口并切换
# 等待新窗口出现并切换
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
wait.until(EC.number_of_windows_to_be(2))
driver.switch_to.window(driver.window_handles[-1])
# 关闭当前标签页后切回原始窗口
driver.close()
driver.switch_to.window(original_handle)
# 窗口大小与位置
driver.maximize_window()
driver.minimize_window()
driver.set_window_size(1920, 1080)
driver.set_window_position(0, 0)
driver.get_window_size() # {"width": 1920, "height": 1080}
driver.get_window_position() # {"x": 0, "y": 0}
框架与 iframe
页面内嵌 iframe 时,必须先切换到 iframe 才能操作其中的元素。
# 按 WebElement 切换
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)
# 按 id 或 name 切换
driver.switch_to.frame("frame_id")
# 按索引切换(页面中第n个iframe,从0开始)
driver.switch_to.frame(0)
# 退出 iframe,回到主文档
driver.switch_to.default_content()
# 退出一层嵌套 iframe(回到父框架)
driver.switch_to.parent_frame()
弹窗处理
原生 JavaScript 弹窗(alert、confirm、prompt)需要通过 switch_to.alert 处理。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待弹窗出现
wait = WebDriverWait(driver, 10)
alert = wait.until(EC.alert_is_present())
alert.text # 弹窗文本内容(str)
alert.accept() # 点击确定(alert/confirm/prompt 均适用)
alert.dismiss() # 点击取消(confirm/prompt)
alert.send_keys("input text") # 向 prompt 输入内容,再 accept()
JavaScript 执行
# 同步执行,返回值即 JS return 的值
result = driver.execute_script("return document.title;")
# 传入 Python 对象作为参数(arguments[0] 对应第一个参数)
driver.execute_script("arguments[0].click();", element)
driver.execute_script("arguments[0].style.border='3px solid red';", element)
# 滚动到元素
driver.execute_script("arguments[0].scrollIntoView(true);", element)
# 滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 修改 input 值(绕过只读限制)
driver.execute_script("arguments[0].value='new value';", input_el)
# 异步执行(适用于 AJAX 回调)
driver.execute_async_script("""
var callback = arguments[arguments.length - 1];
setTimeout(function() { callback('done'); }, 1000);
""")
execute_script() 参数表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
script |
str | 必填 | JavaScript 代码字符串 |
*args |
any | 无 | 传给 JS 的参数,在 JS 中通过 arguments[n] 访问 |
截图
# 截整个页面(视口范围)
driver.save_screenshot("page.png") # 保存到文件
png_bytes = driver.get_screenshot_as_png() # 返回 bytes
base64_str = driver.get_screenshot_as_base64() # 返回 base64 字符串
# 截单个元素
element.screenshot("element.png") # 保存到文件
png_bytes = element.screenshot_as_png # 返回 bytes
base64_str = element.screenshot_as_base64 # 返回 base64 字符串
最佳实践
始终使用显式等待,避免 time.sleep
# 不推荐
time.sleep(3)
# 推荐
wait = WebDriverWait(driver, 10)
el = wait.until(EC.element_to_be_clickable((By.ID, "submit")))
el.click()
用上下文管理器保证浏览器关闭
with webdriver.Chrome(options=options) as driver:
driver.get("https://example.com")
# 异常也会触发 quit()
优先使用 CSS Selector,XPath 作为备选
CSS Selector 执行比 XPath 快,语法更简洁。XPath 在需要向上查找父节点、基于文本定位时更适合。
提取复用 locator
# 将选择器定义为常量,方便维护
USERNAME_INPUT = (By.ID, "username")
LOGIN_BUTTON = (By.CSS_SELECTOR, "button[type='submit']")
driver.find_element(*USERNAME_INPUT).send_keys("admin")
wait.until(EC.element_to_be_clickable(LOGIN_BUTTON)).click()
使用 Page Object 模式组织代码
class LoginPage:
URL = "https://example.com/login"
USERNAME = (By.ID, "username")
PASSWORD = (By.ID, "password")
SUBMIT = (By.CSS_SELECTOR, "button[type='submit']")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def open(self):
self.driver.get(self.URL)
return self
def login(self, username, password):
self.wait.until(EC.visibility_of_element_located(self.USERNAME)).send_keys(username)
self.driver.find_element(*self.PASSWORD).send_keys(password)
self.driver.find_element(*self.SUBMIT).click()
return self
截图辅助调试
try:
element = wait.until(EC.element_to_be_clickable((By.ID, "btn")))
element.click()
except Exception as e:
driver.save_screenshot("debug.png")
raise
常见陷阱与注意事项
StaleElementReferenceException
页面刷新或 DOM 重新渲染后,之前持有的 WebElement 引用失效。解决方案:重新查找元素,或等待 staleness_of 后再查找。
# 错误做法:页面刷新后继续使用旧引用
el = driver.find_element(By.ID, "btn")
driver.refresh()
el.click() # StaleElementReferenceException
# 正确做法:刷新后重新查找
driver.refresh()
wait.until(EC.staleness_of(el)) # 等待旧元素失效
el = wait.until(EC.element_to_be_clickable((By.ID, "btn")))
el.click()
ElementNotInteractableException
元素在 DOM 中存在但不可交互(被遮挡、display:none、opacity:0 等)。解决方案:等待 element_to_be_clickable,或用 JS 点击。
# JS 强制点击(绕过可见性限制,慎用)
driver.execute_script("arguments[0].click();", element)
class 属性包含空格时不能用 By.CLASS_NAME
By.CLASS_NAME 只接受单个 class 名。
# 错误:含空格会抛 InvalidSelectorException
driver.find_element(By.CLASS_NAME, "btn btn-primary")
# 正确:用 CSS Selector
driver.find_element(By.CSS_SELECTOR, ".btn.btn-primary")
XPath 中 @class= 是全量匹配
# 错误:class="btn btn-primary active" 不会被匹配到
//button[@class='btn']
# 正确:用 contains
//button[contains(@class,'btn')]
隐式等待与显式等待不要混用
两者同时存在时,超时时间会以不可预期的方式叠加,导致实际等待时间超出预期。只选择一种策略全局使用。
无头模式下 window-size 需要显式设置
无头模式下浏览器默认窗口极小,可能导致元素不可见或布局异常:
options.add_argument("--window-size=1920,1080")
反爬检测
部分网站检测 navigator.webdriver 属性:
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
options.add_argument("--disable-blink-features=AutomationControlled")
# 注入脚本覆盖 webdriver 属性
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")