Selenium 软件测试入门保姆级

(适配 Python 3.13 + Selenium 4.6+)


一、我踩过的坑(新手必看避坑指南)

  1. Chrome 驱动下载失败,代码卡住不动
    • 原因:国内网络无法访问谷歌官方驱动服务器,Selenium Manager 自动下载超时
    • 解决:
      • 临时方案:用 webdriver-manager 从国内镜像下载驱动
      • 永久方案:让 webdriver-manager 下载一次后,驱动会被缓存,后续原生写法可直接识别
  2. executable_path 参数报错
    • 原因:Selenium 4.6+ 已经废除了 executable_path 参数,不再需要手动指定驱动路径
    • 解决:直接使用 webdriver.Chrome(options=chrome_opt),无需写驱动路径
  3. 浏览器启动慢,要等十几秒才打开
    • 原因:Selenium 启动时会尝试校验版本,国内网络导致超时等待
    • 解决:添加启动参数,跳过校验、禁用多余功能
  4. 旧代码(4.8以下版本)会报错
    • 原因:旧版依赖手动驱动路径,新版有自动驱动管理,且部分参数写法更新(如 --headless 改为 --headless=new
    • 解决:使用适配新版的初始化代码

二、Selenium 环境搭建与浏览器初始化(适配我的版本)

1. 前置依赖安装

# 安装最新版 Selenium
pip install --upgrade selenium

# 可选:解决驱动下载问题的辅助工具(国内推荐)
pip install webdriver-manager

2. 浏览器初始化封装(可复用模板)

新建文件 browser_init.py,复制以下代码:

# -*- coding: utf-8 -*-
# @Description : 适配 Python 3.13 + Selenium 4.6+ 的 Chrome 浏览器初始化模块
import time
import json
import os
import shutil
from dataclasses import dataclass, asdict

from selenium import webdriver
from selenium.webdriver.chrome.options import Options


@dataclass
class WebDriveConf:
    """浏览器全局配置实体类,统一管理所有启动参数"""
    user_data_dir: str = 'D:\\webDriverTempDir'  # 浏览器缓存目录
    is_use_cache: int = 0       # 0=不使用缓存,1=使用缓存
    is_load_page: int = 1       # 0=无界面后台运行,1=弹出窗口
    is_load_img: int = 0        # 0=不加载图片(提速),1=加载图片
    is_ignore_certificate: int = 1  # 1=忽略证书错误,0=校验证书
    is_incognito: int = 1       # 1=无痕模式,0=普通模式

    def to_dict(self):
        """配置转字典,用于写入json文件"""
        return asdict(self)

    def read_json_config(self, file_path: str):
        """读取配置文件,不存在则自动创建默认配置"""
        if not os.path.exists(file_path):
            with open(file_path, "w", encoding="utf-8") as f:
                json.dump(self.to_dict(), f, indent=4, ensure_ascii=False)
        with open(file_path, "r", encoding="utf-8") as f:
            return json.load(f)


class ChromeWebDriver:
    """Chrome浏览器初始化封装类,后续所有自动化测试脚本都调用它"""
    def __init__(self, conf: WebDriveConf):
        self.conf = conf
        self.chrome_opt = Options()

        # --------------------------
        # 核心优化参数(解决国内启动卡顿、模拟真实用户)
        # --------------------------
        self.chrome_opt.add_argument("--no-sandbox")
        self.chrome_opt.add_argument("--disable-gpu")
        self.chrome_opt.add_argument('--window-size=1366,768')
        self.chrome_opt.add_argument('--ignore-certificate-errors')
        self.chrome_opt.add_argument('--disable-extensions')

        # 禁用弹窗 + 设置下载路径(测试常用)
        self.chrome_opt.add_experimental_option('prefs', {
            'profile.default_content_settings.popups': 0,
            'download.default_directory': './downloads'
        })

        # 隐藏自动化控制提示,避免被反爬识别
        self.chrome_opt.add_experimental_option('excludeSwitches', ['enable-automation'])
        self.chrome_opt.add_experimental_option('useAutomationExtension', False)

        # --------------------------
        # 用户数据目录配置(测试用例隔离)
        # --------------------------
        self.chrome_opt.add_argument(f'--user-data-dir={self.conf.user_data_dir}')
        if not self.conf.is_use_cache and os.path.exists(self.conf.user_data_dir):
            shutil.rmtree(self.conf.user_data_dir)
        if not os.path.exists(self.conf.user_data_dir):
            os.makedirs(self.conf.user_data_dir)

        # --------------------------
        # 可选模式配置(根据测试场景选择)
        # --------------------------
        if not self.conf.is_load_page:
            self.chrome_opt.add_argument('--headless=new')  # 新版无界面写法(CI/CD 常用)
        if not self.conf.is_load_img:
            self.chrome_opt.add_argument('--blink-settings=imagesEnabled=false')
        if self.conf.is_incognito:
            self.chrome_opt.add_argument('--incognito')

        # --------------------------
        # 启动浏览器(新版核心:自动驱动管理)
        # --------------------------
        self.chromedriver = webdriver.Chrome(options=self.chrome_opt)

        # 浏览器启动后配置(测试稳定性必备)
        self.chromedriver.set_page_load_timeout(30)  # 页面加载超时30秒


# 测试运行(验证初始化是否成功)
if __name__ == '__main__':
    conf = WebDriveConf()
    config_data = conf.read_json_config("web_driver_conf.json")
    run_conf = WebDriveConf(**config_data)

    browser = ChromeWebDriver(run_conf)
    browser.chromedriver.get("https://www.baidu.com")
    print("浏览器初始化成功!")
    time.sleep(5)
    browser.chromedriver.quit()

三、Selenium 核心学习内容(按软件测试场景排序)

阶段1:基础元素定位(测试核心)

1. 常用定位方式(适配 Selenium 4.x)
定位方式 适用场景 示例代码
By.ID 元素有唯一ID(优先使用,稳定性最高) driver.find_element(By.ID, "kw")
By.NAME 表单元素有name属性 driver.find_element(By.NAME, "wd")
By.XPATH 复杂元素、无ID/NAME的场景 driver.find_element(By.XPATH, '//input[@class="s_ipt"]')
By.CSS_SELECTOR 前端常用,语法简洁 driver.find_element(By.CSS_SELECTOR, "input.s_ipt")
2. 元素等待(测试稳定性必备,替代 time.sleep()
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 显式等待(推荐):最多等待10秒,直到元素出现
wait = WebDriverWait(driver, 10)
search_box = wait.until(EC.presence_of_element_located((By.ID, "kw")))

阶段2:常见页面操作(测试用例高频场景)

1. 元素交互操作
# 输入文本
search_box.send_keys("软件测试学习")
# 点击按钮
search_btn = driver.find_element(By.ID, "su")
search_btn.click()
# 获取元素文本
print("搜索结果标题:", driver.find_element(By.XPATH, '//h3[1]/a').text)
# 获取元素属性
print("搜索框placeholder:", search_box.get_attribute("placeholder"))
2. 窗口与标签页操作
# 获取当前窗口句柄
current_handle = driver.current_window_handle
# 切换到新标签页
driver.switch_to.window(driver.window_handles[-1])
# 关闭当前标签页
driver.close()
# 切换回主窗口
driver.switch_to.window(current_handle)
3. 表单与弹窗处理
# 下拉框选择
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element(By.ID, "city"))
select.select_by_visible_text("北京")

# 处理alert弹窗
alert = driver.switch_to.alert
alert.accept()  # 点击确定
alert.dismiss() # 点击取消
alert.send_keys("输入内容") # 弹窗输入文本

阶段3:进阶测试技巧(解决复杂场景)

1. iframe 切换(页面内嵌框架)
# 切换到iframe
driver.switch_to.frame("iframe_id")
# 操作iframe内的元素
driver.find_element(By.ID, "inner-btn").click()
# 切换回主页面
driver.switch_to.default_content()
2. 鼠标键盘高级操作
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

# 鼠标悬停
ActionChains(driver).move_to_element(element).perform()
# 双击元素
ActionChains(driver).double_click(element).perform()
# 键盘快捷键(Ctrl+A全选文本)
element.send_keys(Keys.CONTROL, "a")
3. 测试用例模板(可直接复用)
from browser_init import ChromeWebDriver, WebDriveConf
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import unittest

class BaiduSearchTest(unittest.TestCase):
    def setUp(self):
        # 初始化浏览器
        conf = WebDriveConf()
        config_data = conf.read_json_config("web_driver_conf.json")
        self.browser = ChromeWebDriver(WebDriveConf(**config_data))
        self.driver = self.browser.chromedriver
        self.wait = WebDriverWait(self.driver, 10)

    def test_baidu_search(self):
        # 1. 打开百度首页
        self.driver.get("https://www.baidu.com")
        # 2. 定位搜索框并输入内容
        search_box = self.wait.until(EC.presence_of_element_located((By.ID, "kw")))
        search_box.send_keys("Selenium软件测试")
        # 3. 点击搜索按钮
        self.wait.until(EC.element_to_be_clickable((By.ID, "su"))).click()
        # 4. 验证结果(断言)
        self.assertIn("Selenium软件测试", self.driver.title)

    def tearDown(self):
        # 关闭浏览器
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()

四、关于 browsermob-proxy 的说明

1. 适用场景

当网页数据通过 Canvas 渲染、无法通过元素定位获取,或者需要抓取接口请求数据做接口测试时,才需要使用代理抓包。

2. 依赖与门槛

  • 需要安装 Java JDK 8+

  • 软件测试中,大部分场景不需要抓包,优先用元素定位+接口测试工具(如Postman)更高效

3.Selenium+browsermob-proxy获取网络请求

第一步:下载 browsermob-proxy
  1. 访问 GitHub 发布页:
    https://github.com/lightbody/browsermob-proxy/releases

  2. 下载最新版的 .zip 文件(例如 browsermob-proxy-2.1.4-bin.zip)。

第二步:安装 Python 模块

在 PyCharm 的 Terminal(或 cmd)中执行:

pip install browsermob-proxy

第三步:编写代码(适配自己的路径)
import time
from selenium import webdriver
from browsermobproxy import Server

# 1. 启动 browsermob-proxy 服务器(路径改成你自己解压的位置)
server = Server("D:\\browsermob-proxy-2.1.4\\bin\\browsermob-proxy.bat")
server.start()
proxy = server.create_proxy()   # 创建一个代理,端口自动分配

# 2. 配置 Chrome 使用该代理
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument(f'--proxy-server={proxy.proxy}')

# 如果你之前有额外的配置(如无痕、不加载图片等),可以继续加上
# chrome_options.add_argument('--incognito')
# chrome_options.add_argument('--blink-settings=imagesEnabled=false')

# 3. 启动浏览器
driver = webdriver.Chrome(options=chrome_options)

# 4. 开始记录网络请求(生成一个 HAR 文件)
proxy.new_har("baidu", options={'captureHeaders': True, 'captureContent': True})

# 5. 访问目标网页
driver.get("https://www.baidu.com")

# 6. 等待页面加载
time.sleep(3)

# 7. 获取所有请求记录
har_data = proxy.har  # 返回一个字典,包含所有请求信息

# 示例:打印所有请求的 URL
for entry in har_data['log']['entries']:
    print(entry['request']['url'])

# 8. 关闭浏览器和代理服务
driver.quit()
server.stop()

重要提示
  1. JDK 17 完全兼容,无需降级。如果运行时提示 Java 找不到,请确保 JDK 的 bin 目录已添加到系统 PATH,或者在命令行能执行 java -version

  2. 路径中的反斜杠:在 Python 字符串中,建议使用双反斜杠 \\ 或原始字符串 r"D:\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat"

  3. 代理生效chrome_options.add_argument(f'--proxy-server={proxy.proxy}') 中的 proxy.proxy 会自动返回类似 localhost:8080 的地址。

  4. 抓取内容:通过 proxy.har 可以获得所有请求的 URL、请求头、响应体等,非常适合抓取 Canvas 渲染所需的数据接口。

更多推荐