基于selenium的自动化测试框架和Page Object (PO) 模型,内置工具快速进行Python Web UI 自动化测试和web自动化脚本开发以及web测试赛的模板。另有一个playwright基于PO模型和高级日志配置的设计模板,主打一个开箱即用。
- 包含内置工具快速构建项目结构
- 对封装selenium进行二次封装
- cookie注入的封装和处理
- requests的简单封装
- web测试赛模板,内置封装的独立方法
pip install pywebauto- 依赖的第三方库
pytest>=8.3.5
selenium>=4.27.1
base_actions.py对应BaseActions类,基础行为封装
base_options.py对应BaseOptions类,基础选项封装
cookie_manager.py对应CookieManager类,cookies管理封装
test_template.py对应TestTemplate类,TestTemplate是web测试赛的测试模板
在selenium中没有鼠标中键的操作,更准确的说是绝大多数现代浏览器都没有这个操作,对于类似于<a>标签实行鼠标中建实际上是进行了一次右击罢了。deepseek给的actions.click(element, button='middle').perform()这个方法是错的,旧版早就移除了。
BaseActions 类是对 Selenium WebDriver 的二次封装,提供了更便捷的元素定位、等待机制、鼠标键盘操作和窗口控制等方法。
初始化 BaseActions 类
- 参数:
driver: Chrome 浏览器驱动对象default_by_strategy: 默认定位策略(1-8)
- 定位策略映射:
- 1: CSS_SELECTOR - 性能好,写法灵活,推荐
- 2: ID - 性能最佳,元素唯一时首选
- 3: XPATH - 功能强大,可遍历DOM,但性能相对较差
- 4: NAME - 主要用于表单元素
- 5: CLASS_NAME - 按CSS类名定位
- 6: LINK_TEXT - 精确匹配超链接文本
- 7: PARTIAL_LINK_TEXT - 部分匹配超链接文本
- 8: TAG_NAME - 按HTML标签名定位
修改默认定位策略
修改隐式等待时间
修改显式等待时间
等待元素出现(使用默认策略)
等待元素出现(自定义策略)
wait_elements_appear(self, by_value: str, mode: int = 1, timeout: int = 10) -> list[WebElement] | bool
等待所有元素出现
检测元素样式是否改变
wait_class_change_ex(self, by_value: str, class_value: str, mode: int = 1, timeout: int = 10) -> bool
检测元素样式是否改变(自定义策略)
安全获取元素的文本内容
释放所有行为(鼠标按住、键盘按住等)
click(self, by_value: str) -> WebElement | bool- 点击元素click_ex(self, by_value: str, mode: int = 3) -> WebElement | bool- 点击元素(自定义策略)js_click(self, by_value: str) -> WebElement | bool- JS点击(绕过遮罩)js_click_ex(self, by_value: str, mode: int = 3) -> WebElement | bool- JS点击(自定义策略)
wait_click(self, by_value: str) -> WebElement | bool- 等待元素出现并点击wait_click_ex(self, by_value: str, mode: int = 1, timeout: int = 10) -> WebElement | bool- 等待点击(自定义策略)js_wait_click(self, by_value: str) -> WebElement | bool- 等待元素出现并JS点击js_wait_click_ex(self, by_value: str, mode: int = 1, timeout: int = 10) -> WebElement | bool- 等待JS点击(自定义策略)
wait_double_click(self, by_value: str) -> WebElement | bool- 双击wait_double_click_ex(self, by_value: str, mode: int = 1, timeout: int = 10) -> WebElement | bool- 双击(自定义策略)right_click(self, element: WebElement) -> WebElement | bool- 右击wait_right_click(self, by_value: str) -> bool- 等待元素出现并右击middle_click(self, element: WebElement) -> bool- 中击move(self, element: WebElement) -> bool- 鼠标悬停wait_move(self, by_value: str) -> bool- 等待元素出现并悬停hold(self, element: WebElement) -> bool- 鼠标按住元素wait_hold(self, by_value: str) -> bool- 等待元素出现并按住click_screen(self, x: int, y: int) -> tuple[int, int]- 点击屏幕指定坐标
等待元素出现并清空内容
等待元素出现并输入文本
send_key_ex(self, by_value: str, mode: int = 3, timeout: int = 10, *keys_to_send: str) -> WebElement | bool
等待元素出现并输入文本(自定义策略)
rewrite_key(self, keys_to_send: str, by_value: str, mode: int = 1, wait_time: int = 0, timeout: int = 10) -> WebElement | bool
等待元素出现,清空后重新输入
max_win(self) -> bool- 最大化窗口min_win(self) -> None- 最小化窗口
close_win(self, target_window_handle: str) -> bool- 关闭指定窗口back_win(self) -> None- 回退上一个网页forward_win(self) -> None- 前进下一个网页
wait_win(self, win_num: int, timeout: int = 5) -> bool- 等待新窗口出现wait_win_ex(self, win_num: int, timeout: int = 5) -> bool- 等待新窗口出现(自定义时间)wait_win_ready(self, timeout: int = 10) -> bool- 等待界面元素加载完成
switch_win(self, target_window_handle: str) -> bool- 切换到指定句柄窗口switch_different_win(self, win_num: int, timeout: int = 5) -> bool- 切换到不同窗口switch_win_for_url(self, url: str) -> bool- 按URL切换到窗口switch_win_for_title(self, window_title: str) -> bool- 按标题切换到窗口switch_win_for_element(self, by_value: str, mode: int = 3) -> bool- 按元素切换到窗口switch_win_ex(self, title: str, url: str, by_value: str, mode: int = 3) -> bool- 多重条件切换窗口
- 基本方法:
method_name - 扩展方法:
method_name_ex(支持自定义参数) - 等待方法:
wait_method_name(包含等待机制) - JavaScript方法:
js_method_name(使用JavaScript实现)
by_value: 元素定位值mode: 定位策略(1-8)timeout: 等待超时时间- 支持可变参数:
*keys_to_send
- 成功:返回元素对象或True
- 失败:返回False
- 多种类型:使用联合类型注解(如
WebElement | bool)
- 捕获常见Selenium异常
- 超时返回False而非抛出异常
- 元素消失或不可交互时返回False
- 性能优先:CSS_SELECTOR > ID > XPATH
- 稳定性优先:优先选择ID、NAME等固定属性
- 灵活性:XPATH功能最强大,支持DOM遍历
- 显式等待:推荐使用,更精准
- 隐式等待:全局设置,影响所有查找
- 混合使用:显式等待为主,隐式等待为辅
- 常规操作:使用基本方法
- 复杂场景:使用
_ex扩展方法 - 特殊需求:使用JavaScript方法
- 稳定性要求高:使用等待方法
这个类库提供了完整的Web自动化操作封装,适合中大型自动化测试项目使用。
以下这里就是playwright的了
本项目仅供个人学习与研究使用,项目中所涉及的技术示例、代码结构、自动化流程等内容,均不保证适用于任何实际业务场景。使用者在使用本项目时应自行确保其行为符合相关法律法规、第三方平台的服务协议与使用政策。
本项目可能涉及对网页交互流程的自动化操作及技术研究,此类研究仅限用于学习测试环境。禁止使用本项目以任何方式规避付费、绕过鉴权、突破平台限制、进行未授权访问、批量抓取内容或其他可能违反法律法规或平台政策的行为。
由于第三方平台具有严格的安全审查机制,使用本项目可能导致账号受限、封禁或其他风险,所有后果均由使用者自行承担,作者不承担任何责任。 作者亦不对本项目可能引发的任何直接或间接损失、商业纠纷、平台处罚、法律风险等负责。
本项目以开源形式提供,使用者可自由复制、修改、再分发或基于本项目进行衍生开发,但必须保留原作者署名,不得删除或隐藏版权信息;同时不得以任何方式暗示作者对使用者的产品、版本或用途提供背书或承担责任。 若使用者将本项目用于商业用途(包括但不限于收费服务、集成到产品中等),因此产生的任何纠纷、法律问题或平台端处理,均与原作者无关。
使用或分发本项目即视为完全理解并同意上述声明。
人话:使用Apache2.0,代码随便用但是作者不承担任何后果,还有分发带上作者名(开源作者基本的尊重)
Playwright个人研究成果/
├─ config/ # 全局配置目录(环境、运行参数)
│ ├─ env.yaml # 环境级配置:base_url、运行环境、浏览器配置等
│ └─ settings.yaml # 框架设置:重试、超时、报告格式、并发等
│
├─ data/ # 测试输入数据(用例需要读取的)
│ ├─ user_settings.toml # 默认用户数据 / 非敏感测试参数
│ └─ 测试数据/ # 用例使用的测试样本数据(json/yaml/csv/xlsx)
│
├─ outputs/ # 运行产物(自动生成,不应提交到 git)
│ ├─ logs/ # 运行日志文件(程序执行输出)
│ ├─ reports/ # 测试报告(HTML/Allure/JUnit 等)
│ ├─ screenshots/ # 失败截图、自动截图
│ ├─ traces/ # Playwright trace 调试文件
│ └─ videos/ # Playwright 自动录制的视频
│
├─ pages/ # Page Object 层(页面类)
│ ├─ __init__.py # 声明 pages 为 Python 包
│ ├─ base_page.py # 所有页面的基类,封装 click/fill/goto/wait 等通用方法
│ ├─ home_page.py # 首页的页面对象(定位器 + 页面行为)
│ └─ login_page.py # 登录页的页面对象(定位器 + login 逻辑)
│
├─ user_data/ # 本地用户私有数据(不建议提交 git)
│ ├─ user_settings.toml # 用户个人敏感配置(token、账号),建议加入 .gitignore
│ └─ logging_config.yaml # 用户覆盖:级别、输出路径、是否打印到控制台等
│
├─ utils/ # 工具库(公共方法、封装组件)
│ ├─ logger.py # 日志模块(格式化、输出、log rotate)
│ └─ playwright_factory.py # Playwright 浏览器/上下文/page 工厂 & 管理封装
│
├─ 开发随笔/ # 开发笔记、实验代码、调试记录(不影响主项目)
│
├─ LICENSES # 协议文件(Apache2.0)
└─ READE.md # 项目说明文档(运行方式、依赖、结构说明)Playwright:框架会话 / 类似驱动连接(入口),实际是node.js
Browser:一次启动的浏览器实例chromium/firefox/webkit(进程/连接)
BrowserContext:隔离会话(像无痕窗口/独立用户)
Page:标签页/页面
sync_playwright()只有start和stop
flowchart TD
A[Playwright 实例<br>(单例,驱动入口)] --> B[Browser 实例 1<br>(如 Chromium 进程)]
A --> C[Browser 实例 N<br>(如 Firefox 进程)]
B --> D[BrowserContext 1-1<br>(隔离会话,如用户A)]
B --> E[BrowserContext 1-2<br>(隔离会话,如用户B)]
C --> F[BrowserContext N-1<br>(独立无痕会话)]
D --> G[Page 1-1-1<br>(标签页)]
D --> H[Page 1-1-2<br>(标签页)]
E --> I[Page 1-2-1]
F --> J[Page N-1-1]
单个谷歌浏览器单个上下文单个标签页的实现
playwright = sync_playwright().start()
browser = playwright.chromium.launch(**launch_options.to_dict())
context = browser.new_context(**context_options.to_dict())
page1 = context.new_page()
page2 = context.new_page()
page3 = context.new_page()
page4 = context.new_page()
context.close()
browser.close()
playwright.stop()-
单例 Factory:同一时刻只允许存在一个活跃的
PlaywrightFactory实例(并发安全);调用close()后会释放单例(_instance=None) -
显式关闭优先:提供
close()作为统一的幂等回收入口;业务代码应显式调用close()(必要时可封装with管理,但当前实现未内置with)。 -
退出兜底:在 Factory 初始化时注册
atexit.register(self.close),在解释器正常退出时自动触发close()做兜底回收(不依赖__del__)。 -
Browser 复用由调用方决定,Factory 保留多策略扩展能力:Factory 提供
new_browser()创建并登记多个 Browser;同时提供get_browser_()返回已登记 Browser 的副本列表,调用方可自行选择复用哪一个 Browser。后续可扩展“默认复用单 Browser / 按 launch 策略复用”的策略方法,但当前默认不自动复用。 -
所有权(ownership)严格:Factory 仅管理并回收由自己创建并登记的 Browser/Context。
new_context(browser=...)要求传入的 browser 必须来自本 Factory(已登记),否则抛出异常;不支持对外部 browser/context 的隐式接管(后续若需要可新增显式adopt_*接口)。 -
回收顺序固定且 stop 至多一次:统一回收顺序为
Contexts → Browsers → Playwright。通过_playwright_flag保证close()幂等:一旦关闭完成,后续再调用close()不会重复 stop。 -
注册表线程安全,关闭在锁外执行:所有创建/登记/移除(Browser/Context registry)操作均在同一把可重入锁保护下完成;
close()在锁内完成“置状态 + 拷贝 + 清空 registry”,随后在锁外执行实际close()/stop(),避免阻塞其他线程。 -
Page 不纳入生命周期管理:Factory 仅提供
new_page(context)的便捷创建,不追踪 Page;Page 的回收依赖context.close()的级联行为,如需 Page 级别的特殊回收策略由上层业务实现。
ConfigManager实现了线程安全的单例模式,采用了业界 认的双重检查锁定DCL(Double-Checked Locking)模式- 使用配置层叠(Configuration Cascading),设计让高优先级的配置文件去覆盖(Override)低优先级配置文件中的同名配置项。
- 硬编码写好默认的日志配置,防止日志配置文件
logging_config.yaml错误导致日志记录器无法记录配置文件错误部分的崩溃日志。 - 设置配置使用
settings.toml,构launch_options.py和context_options.py两个有关浏览器配置的数据模型。
流程说明:
- 无论配置是否解析成功,都会先加载硬编码默认配置
- 配置解析失败时才检查/创建日志路径
- 错误信息会同时写入日志和控制台
- 配置解析成功则进入正常业务逻辑
- 加载默认的硬编码配置(程序内置配置)
- 尝试解析外部配置文件
- 判断:配置文件能否成功解析?
- 能解析 → 进入正常业务流程
- 不能解析 → 进入错误处理流程
- 检查日志输出路径是否存在
- 判断:日志路径是否存在?
- 存在 → 直接记录错误
- 不存在 → 创建日志输出路径
- 抛出致命错误
- 同时执行两个操作:
- 将错误信息写入日志文件
- 将错误信息输出到控制台
graph TD
A[开始] --> B[加载默认硬编码配置]
B --> C{配置文件能否解析?}
C -->|能| D[继续正常业务流程]
C -->|不能| E{检查日志输出路径是否存在}
E -->|存在| F[记录配置解析错误]
E -->|不存在| G[创建日志输出路径]
G --> F
F --> H[抛出致命错误]
H --> I[写入日志文件]
H --> J[输出到控制台]
I --> K[结束]
J --> K
D --> L[...正常业务逻辑...]
下面是一个 清晰表格,整理了 Python logging.config.dictConfig() 可以自动解析的常用字段及其作用(基于官方文档 logging.config.dictConfig 规范)。
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| version | int |
必需字段。当前规范版本,必须为 1。 |
| disable_existing_loggers | bool |
是否禁用已有的 logger,默认 True。 |
| formatters | dict |
定义 formatter 的集合。 |
| filters | dict |
定义 filter 的集合。 |
| handlers | dict |
定义 handler 的集合。 |
| loggers | dict |
为每个非 root logger 进行配置。 |
| root | dict |
配置 root logger。 |
| incremental | bool |
是否为增量更新配置(一般用不到)。 |
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| format | str |
格式化日志消息的格式字符串。 |
| datefmt | str |
时间格式。 |
| style | '%' / '{' / '$' |
指定格式化风格(默认 %)。 |
| class | str |
自定义 Formatter 类路径(可选)。 |
| validate | bool |
是否验证参数(Python3.8+ 默认 True)。 |
每个 handler 的字段如下:
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| class | str |
必填。Handler 类路径,例如 "logging.StreamHandler"。 |
| level | str |
设置 handler 级别,例如 "INFO"。 |
| formatter | str |
指向 formatters 中的某个 formatter 名称。 |
| filters | list |
使用的 filter 名称列表。 |
| 其他参数(如 filename 等) | any |
handler 类本身要求的参数。例如 FileHandler 需要 filename。 |
示例:
FileHandler 会自动解析 filename、mode 等字段。
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| class | str |
必填。Filter 的完整类路径,例如 "logging.Filter"。 |
| 其他参数 | any |
自定义 Filter 的参数。 |
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| level | str |
logger 日志级别。 |
| handlers | list |
使用的 handler 名称列表。 |
| propagate | bool |
是否向父 logger 传播消息。 |
| filters | list |
绑定到 logger 的 filter。 |
| 字段名 | 类型 | 作用说明 |
|---|---|---|
| level | str |
root logger 的日志级别。 |
| handlers | list |
root logger 使用的 handlers。 |
{
"version": 1,
"formatters": {
"simple": {
"format": "%(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple"
}
},
"root": {
"level": "INFO",
"handlers": ["console"]
}
}缺点是没法校验yaml配置路径等字段的有效性,仅仅能用默认配置好的路径(硬编码检查确保默认配置路径有效),必须结合ConfigManager使用,不结合使用设计的这个日志配置就没有什么意义了。
# 导入配置管理模块
from utils import ConfigManager
# 创建配置管理器实例(这是个单例)
config_manager = ConfigManager()
# 层叠覆盖原来的日志配置
logger_manager.use_logging_config(config_manager)如果真的仅仅用日志体系,但是不要层叠覆盖就得确保根目录下有outputs/logs这个文件夹路径(日志放置的地方)。
from utils import logger_manager, info
info("第一条程序已经加载完成,日志记录器、全局异常捕获已完成")| 参数名 | 分类 | 描述 (中文) |
|---|---|---|
**--disable-logging** |
性能与日志 | 禁用或减少浏览器内部的日志输出,减轻文件 I/O 负担。 |
**--mute-audio** |
性能与日志 | 确保浏览器静音,减少资源占用。 |
**--disable-extensions** |
资源节约 | 禁用浏览器扩展,减少资源消耗。 |
**--disable-default-apps** |
资源节约 | 禁用 Chrome 的默认应用程序。 |
**--disable-background-networking** |
资源节约 | 阻止浏览器进行任何后台网络活动。 |
**--disable-sync** |
资源节约 | 禁用 Chrome 的同步功能。 |
**--disable-domain-reliability** |
资源节约 | 禁用域名可靠性监测。 |
**--no-first-run** |
资源节约 | 跳过首次运行向导。 |
**--no-sandbox** |
兼容性与稳定性 | 禁用沙盒模式(常用于 Linux/容器环境)。 |
**--disable-setuid-sandbox** |
兼容性与稳定性 | 禁用 setuid 沙盒(用于解决 Linux 权限问题)。 |
**--disable-dev-shm-usage** |
兼容性与稳定性 | 禁用 /dev/shm 共享内存(优化内部内存使用)。 |
**--disable-popup-blocking** |
兼容性与稳定性 | 禁用弹出窗口阻止程序(提高稳定性)。 |
**--disable-infobars** |
兼容性与稳定性 | 禁用顶部的“受自动化软件控制”信息条。 |
**--disable-gpu** |
兼容性与渲染 | 禁用 GPU 硬件加速,避免图形相关 Bug,尤其在无头模式中。 |
**--disable-software-rasterizer** |
兼容性与渲染 | 禁用了 CPU 上的备用渲染方案。 |
**--headless=new** |
无头模式 | 启用新的无头模式(不渲染图形界面)。 |