一、Python-Selenium原理解析

Python-Selenium环境准备

一、常见浏览器驱动地址:

chromedriver驱动地址:

  1. https://googlechromelabs.github.io/chrome-for-testing/
  2. https://registry.npmmirror.com/binary.html?path=chromedriver/

edge驱动地址:

  1. https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

selenium运行流程:

  1. 测试脚本编写:测试人员编写基于 Selenium 库的自动化测试脚本,定义测试步骤和预期结果。
  2. 启动浏览器驱动:根据测试脚本指定的浏览器类型,启动相应的浏览器驱动程序。
  3. 选择浏览器类型:根据脚本配置,确定要使用的浏览器,如 Chrome、Firefox 等,不同浏览器对应不同的驱动程序。
  4. 打开目标网页:驱动程序根据测试脚本中的 URL,在对应的浏览器中打开目标网页。
  5. 定位网页元素:使用 Selenium 提供的定位方法(如 ID、XPath、CSS 选择器等),在打开的网页中定位需要操作的元素。
  6. 操作类型选择:确定对定位到的元素执行何种操作,如点击、输入文本或获取元素信息等。
  7. 执行操作:驱动程序模拟用户行为,在网页上执行相应的操作。
  8. 等待页面响应:操作执行后,等待页面加载或响应,确保页面状态稳定,以便进行后续操作。
  9. 判断是否有更多操作:检查测试脚本中是否还有其他操作需要执行,如果有,则返回定位网页元素步骤继续执行;如果没有,则关闭浏览器,结束测试流程。
    一、Python-Selenium原理解析_第1张图片

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 解析请求并启动浏览器
  1. 验证请求合法性:检查 capabilities 是否支持(如 browserName 是否为 chrome)。
  2. 启动 Chrome 浏览器进程:根据 goog:chromeOptions 中的配置(如 --headless=new 无头模式),调用 Chrome 的可执行文件(如 C:\Program Files\Google\Chrome\Application\chrome.exe)。
  3. 建立与浏览器的通信:通过 Chrome 的 DevTools Protocol(CDP)与浏览器建立 WebSocket 连接(通常监听 localhost:随机端口)。
  4. 生成 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(成功)

六、时序图

  1. 用户代码(UC):代表用户编写的包含 Selenium 代码的程序。
  2. Chrome 类(Chrome):负责与chromedriver进程交互,初始化相关环境。
  3. chromedriver 进程(CDP):作为 Selenium 与浏览器之间通信的桥梁,处理各种请求并控制浏览器。
  4. RemoteWebDriver(RWD):Selenium 中用于远程控制浏览器的关键组件,负责协调各部分交互并处理请求和响应。
  5. 浏览器(Browser):实际执行网页导航等操作的应用程序。
    一、Python-Selenium原理解析_第2张图片

你可能感兴趣的:(Selenium,selenium,python)