用户脚本 → Selenium Python 客户端库 → ChromeDriver 驱动进程 → Chrome 浏览器
webdriver.Chrome()
到浏览器启动 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 请求。# 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)
# 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"]
capabilities
是否支持(如 browserName
是否为 chrome
)。goog:chromeOptions
中的配置(如 --headless=new
无头模式),调用 Chrome 的可执行文件(如 C:\Program Files\Google\Chrome\Application\chrome.exe
)。localhost:随机端口
)。a1b2c3d4e5f6g7h8i9j0
)。 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 协议状态码)
}
客户端收到响应后,会提取 sessionId
并保存到 driver.session_id
属性中。后续所有操作(如 driver.get(url)
)都会基于此 ID 与 chromedriver
通信。
driver.get(url)
为例 driver.get(url)
的执行链路可总结为:用户代码 → WebDriver.get() → RemoteWebDriver.execute() → CommandExecutor 发送 HTTP 请求 → ChromeDriver 处理 → 浏览器执行导航 → 响应返回客户端
driver.get(url)
到请求生成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 方法发送命令
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")
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"))
ChromeDriver 首先检查请求路径中的 sessionId
(如 a1b2c3d4
)是否存在且有效。若无效(如会话已关闭),返回 invalid session id
错误。
ChromeDriver 与 Chrome 浏览器通过 CDP(DevTools Protocol)通信。对于 get
操作,ChromeDriver 会调用 CDP 的 Page.navigate
方法:
{
"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 操作无返回值)
用户代码: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 (成功) |