Python-Selenium环境准备
一、常见浏览器驱动地址:
chromedriver驱动地址:
- https://googlechromelabs.github.io/chrome-for-testing/
- https://registry.npmmirror.com/binary.html?path=chromedriver/
edge驱动地址:
- https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
selenium运行流程:
- 测试脚本编写:测试人员编写基于 Selenium 库的自动化测试脚本,定义测试步骤和预期结果。
- 启动浏览器驱动:根据测试脚本指定的浏览器类型,启动相应的浏览器驱动程序。
- 选择浏览器类型:根据脚本配置,确定要使用的浏览器,如 Chrome、Firefox 等,不同浏览器对应不同的驱动程序。
- 打开目标网页:驱动程序根据测试脚本中的 URL,在对应的浏览器中打开目标网页。
- 定位网页元素:使用 Selenium 提供的定位方法(如 ID、XPath、CSS 选择器等),在打开的网页中定位需要操作的元素。
- 操作类型选择:确定对定位到的元素执行何种操作,如点击、输入文本或获取元素信息等。
- 执行操作:驱动程序模拟用户行为,在网页上执行相应的操作。
- 等待页面响应:操作执行后,等待页面加载或响应,确保页面状态稳定,以便进行后续操作。
- 判断是否有更多操作:检查测试脚本中是否还有其他操作需要执行,如果有,则返回定位网页元素步骤继续执行;如果没有,则关闭浏览器,结束测试流程。

selenium运行原理(包含源代码):
一、核心组件与调用链路
用户脚本 → Selenium Python 客户端库 → ChromeDriver 驱动进程 → Chrome 浏览器
二、初始化阶段:从 webdriver.Chrome() 到浏览器启动
1. 查找并启动 ChromeDriver
Selenium 客户端库(selenium 包)的 webdriver 模块中,Chrome 类继承自 RemoteWebDriver,其初始化逻辑如下
# selenium/webdriver/chrome/webdriver.py
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.remote.webdriver import WebDriver
class Chrome(WebDriver):
def __init__(self, executable_path="chromedriver", options=None, service=None):
# 初始化 Chrome 服务(控制 ChromeDriver 进程)
self.service = service or Service(executable_path)
self.service.start() # 启动 chromedriver 可执行文件(默认监听 localhost:9515)
# 构造与 ChromeDriver 通信的基础 URL
remote_server_url = f"http://localhost:{self.service.port}"
# 调用父类 RemoteWebDriver 的初始化方法,建立与驱动的连接
super().__init__(command_executor=remote_server_url, options=options)
关键逻辑:
Service类负责通过subprocess启动chromedriver进程(可通过executable_path指定驱动路径,若未指定则从环境变量查找)。chromedriver启动后默认监听本地端口(如 9515),等待接收 HTTP 请求。
2. 创建浏览器会话(Session)
步骤 1:查找并启动 ChromeDriver 进程
# selenium/webdriver/chrome/webdriver.py(简化版)
# Selenium 客户端库(selenium 包)的 Chrome 类在初始化时,会首先启动 chromedriver 可执行文件(浏览器驱动)。这一步由 Service 类完成:
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.remote.webdriver import WebDriver
class Chrome(WebDriver):
def __init__(self, executable_path="chromedriver", options=None):
# 初始化 Chrome 服务(控制 chromedriver 进程)
self.service = Service(executable_path)
self.service.start() # 启动 chromedriver 进程
# 构造与 ChromeDriver 通信的基础 URL(默认 http://localhost:9515)
remote_server_url = f"http://localhost:{self.service.port}"
# 调用父类初始化,传入通信 URL 和浏览器配置(options)
super().__init__(command_executor=remote_server_url, options=options)
步骤 2:构造 Session 请求(客户端 → ChromeDriver)
# selenium/webdriver/remote/webdriver.py(简化版)
# 启动 chromedriver 后,客户端需要发送 创建会话的 HTTP 请求。这一步由父类 RemoteWebDriver 的 start_session 方法完成:
class WebDriver:
def __init__(self, command_executor, options=None):
self.command_executor = command_executor # 如 http://localhost:9515
self.start_session(options) # 触发 Session 创建
def start_session(self, capabilities):
# 构造符合 WebDriver 协议的请求参数
request_params = {
"capabilities": {
"alwaysMatch": capabilities.to_capabilities() # 浏览器配置(如无头模式)
}
}
# 发送 HTTP 请求:创建会话(POST /session)
response = self.execute("newSession", request_params)
# 提取 Session ID(后续所有操作的核心标识)
self.session_id = response["value"]["sessionId"]
步骤 3:ChromeDriver 解析请求并启动浏览器
- 验证请求合法性:检查
capabilities是否支持(如browserName是否为chrome)。 - 启动 Chrome 浏览器进程:根据
goog:chromeOptions中的配置(如--headless=new无头模式),调用 Chrome 的可执行文件(如C:\Program Files\Google\Chrome\Application\chrome.exe)。 - 建立与浏览器的通信:通过 Chrome 的 DevTools Protocol(CDP)与浏览器建立 WebSocket 连接(通常监听
localhost:随机端口)。 - 生成 Session ID:为当前会话分配一个全局唯一的字符串(如
a1b2c3d4e5f6g7h8i9j0)。
步骤 4:返回 Session 信息(ChromeDriver → 客户端)
chromedriver 完成浏览器启动后,会返回一个 JSON 格式的响应
{
"value": {
"sessionId": "a1b2c3d4e5f6g7h8i9j0", # 会话唯一标识
"capabilities": {
"browserName": "chrome",
"browserVersion": "120.0.6099.109",
"goog:chromeOptions": {
"debuggerAddress": "localhost:53456" # CDP 调试地址(供 chromedriver 内部使用)
}
}
},
"sessionId": "a1b2c3d4e5f6g7h8i9j0",
"status": 0 # 0 表示成功(W3C WebDriver 协议状态码)
}
步骤 5:客户端保存 Session ID 并完成初始化
客户端收到响应后,会提取 sessionId 并保存到 driver.session_id 属性中。后续所有操作(如 driver.get(url))都会基于此 ID 与 chromedriver 通信。
三、操作执行阶段:以 driver.get(url) 为例
driver.get(url) 的执行链路可总结为:用户代码 → WebDriver.get() → RemoteWebDriver.execute() → CommandExecutor 发送 HTTP 请求 → ChromeDriver 处理 → 浏览器执行导航 → 响应返回客户端
步骤1:用户调用:从 driver.get(url) 到请求生成
1. WebDriver.get() 方法(用户直接调用)
WebDriver 类(selenium/webdriver/remote/webdriver.py)定义了 get 方法,它是用户操作的入口:
# selenium/webdriver/remote/webdriver.py
class WebDriver:
def get(self, url):
"""导航到指定 URL"""
self.execute("get", {"url": url}) # 调用 execute 方法发送命令
2. WebDriver.execute() 方法(生成请求核心)
# selenium/webdriver/remote/webdriver.py
class WebDriver:
def execute(self, command, params=None):
params = params or {}
# 构造请求路径(格式:/session/{sessionId}/{command})
path = f"/session/{self.session_id}/{command}"
# 调用 CommandExecutor 发送 HTTP 请求
response = self.command_executor.execute(
command=command,
params=params,
path=path
)
# 处理响应(如抛出异常或返回结果)
if response.get("status"):
raise self.exceptions.get(response["status"])(response["value"])
return response.get("value")
3. CommandExecutor 发送 HTTP 请求(底层通信)
# selenium/webdriver/remote/remote_connection.py
class RemoteConnection:
def execute(self, command, params, path):
# 构造完整请求 URL(如 http://localhost:9515/session/a1b2c3d4/get)
url = f"{self.remote_server_url}{path}"
# 配置 HTTP 请求(POST 方法,JSON 格式请求体)
method = "POST"
body = json.dumps(params).encode("utf-8")
# 使用 urllib3 发送请求(底层是 HTTP 客户端)
response = self._request(method, url, body=body)
# 解析响应(从 JSON 转换为 Python 字典)
return json.loads(response.body.decode("utf-8"))
步骤2:ChromeDriver 处理请求:从 HTTP 到浏览器指令
-
解析请求并验证 Session ID
ChromeDriver 首先检查请求路径中的
sessionId(如a1b2c3d4)是否存在且有效。若无效(如会话已关闭),返回invalid session id错误。 -
将 WebDriver 命令转换为 DevTools Protocol(CDP)指令
ChromeDriver 与 Chrome 浏览器通过 CDP(DevTools Protocol)通信。对于
get操作,ChromeDriver 会调用 CDP 的Page.navigate方法: -
等待浏览器完成导航
步骤3:响应返回:从浏览器到客户端
-
ChromeDriver 生成响应
{ "value": {}, # get 操作无返回值(导航成功时) "sessionId": "a1b2c3d4", "status": 0 # 0 表示成功(W3C 状态码) } -
客户端解析响应并处理
客户端收到响应后,
RemoteConnection.execute()方法会解析 JSON 响应,并返回给WebDriver.execute()方法:# selenium/webdriver/remote/webdriver.py class WebDriver: def execute(self, command, params=None): response = self.command_executor.execute(...) if response.get("status"): # 检查是否有错误状态码 # 根据状态码抛出对应的异常(如 NoSuchWindowException、InvalidArgumentException) raise self.exceptions.get(response["status"])(response["value"]) return response.get("value") # 成功时返回空(get 操作无返回值)步骤4:完整源代码解析过程
用户代码:driver.get("https://example.com") ↓ WebDriver.get() → 调用 execute("get", {"url": url}) ↓ WebDriver.execute() → 构造路径 /session/{sessionId}/get,调用 command_executor.execute() ↓ RemoteConnection.execute() → 发送 HTTP POST 请求到 http://localhost:9515/session/{sessionId}/get,请求体 {"url": url} ↓ ChromeDriver → 解析请求,通过 CDP 发送 Page.navigate 指令到浏览器 ↓ 浏览器 → 加载 URL,完成导航 ↓ ChromeDriver → 生成响应 JSON(如 {"value": {}, "status": 0}) ↓ RemoteConnection.execute() → 解析响应 JSON ↓ WebDriver.execute() → 检查状态码,无错误则返回
四、异常处理与资源释放
当用户调用 driver.quit() 时,客户端会发送 DELETE /session/{sessionId} 请求,通知 chromedriver 关闭浏览器并终止自身进程:
# selenium/webdriver/remote/webdriver.py
class WebDriver:
def quit(self):
try:
self.execute("quit") # 发送关闭会话请求
finally:
self.service.stop() # 终止 chromedriver 进程
五、调用链关键数据流动
| 阶段 | 关键数据 | 示例值 / 格式 |
|---|---|---|
| 启动 chromedriver | 驱动监听 URL | http://localhost:9515 |
| 创建 Session 请求 | 请求体(浏览器配置) | {"capabilities": {"alwaysMatch": ...}} |
| Session 响应 | Session ID | "a1b2c3d4" |
| get 操作请求 | 请求 URL | http://localhost:9515/session/a1b2c3d4/get |
| get 操作请求体 | 导航 URL | {"url": "https://example.com"} |
| 浏览器 CDP 指令 | Page.navigate 参数 | {"url": "https://example.com"} |
| get 操作响应 | 响应状态码 | 0(成功) |
六、时序图
- 用户代码(UC):代表用户编写的包含 Selenium 代码的程序。
- Chrome 类(Chrome):负责与
chromedriver进程交互,初始化相关环境。 - chromedriver 进程(CDP):作为 Selenium 与浏览器之间通信的桥梁,处理各种请求并控制浏览器。
- RemoteWebDriver(RWD):Selenium 中用于远程控制浏览器的关键组件,负责协调各部分交互并处理请求和响应。
- 浏览器(Browser):实际执行网页导航等操作的应用程序。

896

被折叠的 条评论
为什么被折叠?



