u2-webview 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- u2_webview-0.1.0/LICENSE +21 -0
- u2_webview-0.1.0/PKG-INFO +150 -0
- u2_webview-0.1.0/README.md +123 -0
- u2_webview-0.1.0/setup.cfg +4 -0
- u2_webview-0.1.0/setup.py +28 -0
- u2_webview-0.1.0/u2_webview/__init__.py +4 -0
- u2_webview-0.1.0/u2_webview/core.py +111 -0
- u2_webview-0.1.0/u2_webview.egg-info/PKG-INFO +150 -0
- u2_webview-0.1.0/u2_webview.egg-info/SOURCES.txt +10 -0
- u2_webview-0.1.0/u2_webview.egg-info/dependency_links.txt +1 -0
- u2_webview-0.1.0/u2_webview.egg-info/requires.txt +3 -0
- u2_webview-0.1.0/u2_webview.egg-info/top_level.txt +1 -0
u2_webview-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YuYoungG
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: u2_webview
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An extension for uiautomator2 to support WebView automation via DrissionPage
|
|
5
|
+
Home-page: https://github.com/YuYoungG/uiautomator2-webview
|
|
6
|
+
Author: YuYoungG
|
|
7
|
+
Author-email: younggg2218@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: uiautomator2>=2.16.0
|
|
15
|
+
Requires-Dist: DrissionPage>=4.1.0
|
|
16
|
+
Requires-Dist: adbutils>=0.11.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
Dynamic: summary
|
|
27
|
+
|
|
28
|
+
# u2_webview
|
|
29
|
+
|
|
30
|
+
**u2_webview** 是一个专为 `uiautomator2` 定制的混合应用(Hybrid App)自动化扩展库。它通过集成 `DrissionPage` ,实现了对移动端 WebView 的“无驱动”(Driverless)接管。
|
|
31
|
+
|
|
32
|
+
## 核心优势
|
|
33
|
+
|
|
34
|
+
- **免驱动接管 (Driverless)**:不同于传统的 Selenium/Appium,本库无需下载、配置或匹配特定版本的 `chromedriver`。它通过 CDP 协议直接与 WebView 通信,彻底告别驱动版本不匹配的烦恼。
|
|
35
|
+
- **Flask 扩展模式设计**:遵循 Flask 插件设计哲学,支持“应用工厂”模式,实现与 `uiautomator2.Device` 实例的完全解耦。
|
|
36
|
+
- **高性能通信**:基于 `adbutils` 建立高效的端口转发隧道,确保 H5 操作的高响应速度与稳定性。
|
|
37
|
+
- **API 极简**:只需一个属性 `.current_page`,即可像操作浏览器一样操作手机内的 H5 页面。
|
|
38
|
+
|
|
39
|
+
## 环境要求
|
|
40
|
+
|
|
41
|
+
- **Python**: 3.8 或更高版本
|
|
42
|
+
- **Android 设备**: 需开启 ADB 调试
|
|
43
|
+
- **被测 App**: WebView 必须开启调试模式(`setWebContentsDebuggingEnabled(true)`)
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
通过 PyPI 直接安装:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install u2_webview
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
或者从源码本地安装(开发模式):
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
git clone [https://github.com/YuYoungG/uiautomator2-webview.git](https://github.com/YuYoungG/uiautomator2-webview.git)
|
|
57
|
+
cd uiautomator2-webview
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 使用指南
|
|
62
|
+
|
|
63
|
+
本库支持两种初始化模式,以适配不同的框架架构。
|
|
64
|
+
|
|
65
|
+
### 1. 基础用法 (直接绑定)
|
|
66
|
+
|
|
67
|
+
适用于简单的脚本测试。
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
import uiautomator2 as u2
|
|
71
|
+
from u2_webview import Webview
|
|
72
|
+
|
|
73
|
+
# 连接设备
|
|
74
|
+
d = u2.connect()
|
|
75
|
+
|
|
76
|
+
# 实例化扩展并绑定设备
|
|
77
|
+
webview = Webview(d)
|
|
78
|
+
|
|
79
|
+
# 访问 H5 页面属性 (会自动触发 attach)
|
|
80
|
+
print(f"当前 H5 标题: {webview.current_page.title}")
|
|
81
|
+
|
|
82
|
+
# 使用 DrissionPage 语法进行操作
|
|
83
|
+
webview.current_page.ele('text:登录').click()
|
|
84
|
+
|
|
85
|
+
# 测试结束,清理资源
|
|
86
|
+
webview.detach()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. 工厂模式用法 (推荐用于大型框架)
|
|
90
|
+
|
|
91
|
+
类似于 Flask 的 `init_app` 模式,适合在设备对象尚未完全确定时预定义扩展。
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
from u2_webview import Webview
|
|
95
|
+
import uiautomator2 as u2
|
|
96
|
+
|
|
97
|
+
# 全局预定义扩展对象
|
|
98
|
+
webview = Webview()
|
|
99
|
+
|
|
100
|
+
def run_test(serial):
|
|
101
|
+
d = u2.connect(serial)
|
|
102
|
+
|
|
103
|
+
# 在运行时绑定具体设备
|
|
104
|
+
webview.init_device(d)
|
|
105
|
+
|
|
106
|
+
# 接管并操作
|
|
107
|
+
page = webview.current_page
|
|
108
|
+
page.actions.move_to('.slider').click()
|
|
109
|
+
|
|
110
|
+
webview.detach()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 核心 API 参考
|
|
114
|
+
|
|
115
|
+
### `Webview(d=None)`
|
|
116
|
+
|
|
117
|
+
构造函数。可选参数 `d` 为 `uiautomator2.connect()` 返回的对象。
|
|
118
|
+
|
|
119
|
+
### `webview.init_device(d)`
|
|
120
|
+
|
|
121
|
+
将扩展实例绑定到特定的 `uiautomator2` 设备对象。
|
|
122
|
+
|
|
123
|
+
### `webview.attach(timeout=20)`
|
|
124
|
+
|
|
125
|
+
手动建立与手机 WebView 的调试连接。成功后返回 `DrissionPage.Chromium` 对象。
|
|
126
|
+
|
|
127
|
+
### `webview.current_page` (Property)
|
|
128
|
+
|
|
129
|
+
**核心属性**。获取当前活跃的标签页对象(`ChromiumTab`)。
|
|
130
|
+
|
|
131
|
+
- *注:若未连接,访问此属性将自动调用 `attach()`。*
|
|
132
|
+
|
|
133
|
+
### `webview.detach()`
|
|
134
|
+
|
|
135
|
+
断开 CDP 连接并移除 ADB 端口转发映射,释放系统资源。
|
|
136
|
+
|
|
137
|
+
## 常见问题
|
|
138
|
+
|
|
139
|
+
**Q: 为什么找不到 WebView Socket?**
|
|
140
|
+
|
|
141
|
+
1. 请确认 App 已经进入了包含 H5 的 Activity。
|
|
142
|
+
2. 请确认 App 源码中开启了 WebView 调试:`WebView.setWebContentsDebuggingEnabled(true);`。如果是第三方 App,可能需要使用 Xposed 模块(如 WebViewDebugHook)强制开启。
|
|
143
|
+
|
|
144
|
+
**Q: 是否支持多设备并行?** 支持。每个 `Webview` 实例在初始化时都会自动分配一个独立的本地空闲端口,多台手机同时运行不会发生冲突。
|
|
145
|
+
|
|
146
|
+
## 开源协议
|
|
147
|
+
|
|
148
|
+
本项目采用 [MIT License](https://opensource.org/licenses/MIT) 协议。
|
|
149
|
+
|
|
150
|
+
**贡献与支持**: 欢迎提交 Issue 或 Pull Request 来完善本项目。
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# u2_webview
|
|
2
|
+
|
|
3
|
+
**u2_webview** 是一个专为 `uiautomator2` 定制的混合应用(Hybrid App)自动化扩展库。它通过集成 `DrissionPage` ,实现了对移动端 WebView 的“无驱动”(Driverless)接管。
|
|
4
|
+
|
|
5
|
+
## 核心优势
|
|
6
|
+
|
|
7
|
+
- **免驱动接管 (Driverless)**:不同于传统的 Selenium/Appium,本库无需下载、配置或匹配特定版本的 `chromedriver`。它通过 CDP 协议直接与 WebView 通信,彻底告别驱动版本不匹配的烦恼。
|
|
8
|
+
- **Flask 扩展模式设计**:遵循 Flask 插件设计哲学,支持“应用工厂”模式,实现与 `uiautomator2.Device` 实例的完全解耦。
|
|
9
|
+
- **高性能通信**:基于 `adbutils` 建立高效的端口转发隧道,确保 H5 操作的高响应速度与稳定性。
|
|
10
|
+
- **API 极简**:只需一个属性 `.current_page`,即可像操作浏览器一样操作手机内的 H5 页面。
|
|
11
|
+
|
|
12
|
+
## 环境要求
|
|
13
|
+
|
|
14
|
+
- **Python**: 3.8 或更高版本
|
|
15
|
+
- **Android 设备**: 需开启 ADB 调试
|
|
16
|
+
- **被测 App**: WebView 必须开启调试模式(`setWebContentsDebuggingEnabled(true)`)
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
通过 PyPI 直接安装:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
pip install u2_webview
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
或者从源码本地安装(开发模式):
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
git clone [https://github.com/YuYoungG/uiautomator2-webview.git](https://github.com/YuYoungG/uiautomator2-webview.git)
|
|
30
|
+
cd uiautomator2-webview
|
|
31
|
+
pip install -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 使用指南
|
|
35
|
+
|
|
36
|
+
本库支持两种初始化模式,以适配不同的框架架构。
|
|
37
|
+
|
|
38
|
+
### 1. 基础用法 (直接绑定)
|
|
39
|
+
|
|
40
|
+
适用于简单的脚本测试。
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
import uiautomator2 as u2
|
|
44
|
+
from u2_webview import Webview
|
|
45
|
+
|
|
46
|
+
# 连接设备
|
|
47
|
+
d = u2.connect()
|
|
48
|
+
|
|
49
|
+
# 实例化扩展并绑定设备
|
|
50
|
+
webview = Webview(d)
|
|
51
|
+
|
|
52
|
+
# 访问 H5 页面属性 (会自动触发 attach)
|
|
53
|
+
print(f"当前 H5 标题: {webview.current_page.title}")
|
|
54
|
+
|
|
55
|
+
# 使用 DrissionPage 语法进行操作
|
|
56
|
+
webview.current_page.ele('text:登录').click()
|
|
57
|
+
|
|
58
|
+
# 测试结束,清理资源
|
|
59
|
+
webview.detach()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. 工厂模式用法 (推荐用于大型框架)
|
|
63
|
+
|
|
64
|
+
类似于 Flask 的 `init_app` 模式,适合在设备对象尚未完全确定时预定义扩展。
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
from u2_webview import Webview
|
|
68
|
+
import uiautomator2 as u2
|
|
69
|
+
|
|
70
|
+
# 全局预定义扩展对象
|
|
71
|
+
webview = Webview()
|
|
72
|
+
|
|
73
|
+
def run_test(serial):
|
|
74
|
+
d = u2.connect(serial)
|
|
75
|
+
|
|
76
|
+
# 在运行时绑定具体设备
|
|
77
|
+
webview.init_device(d)
|
|
78
|
+
|
|
79
|
+
# 接管并操作
|
|
80
|
+
page = webview.current_page
|
|
81
|
+
page.actions.move_to('.slider').click()
|
|
82
|
+
|
|
83
|
+
webview.detach()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 核心 API 参考
|
|
87
|
+
|
|
88
|
+
### `Webview(d=None)`
|
|
89
|
+
|
|
90
|
+
构造函数。可选参数 `d` 为 `uiautomator2.connect()` 返回的对象。
|
|
91
|
+
|
|
92
|
+
### `webview.init_device(d)`
|
|
93
|
+
|
|
94
|
+
将扩展实例绑定到特定的 `uiautomator2` 设备对象。
|
|
95
|
+
|
|
96
|
+
### `webview.attach(timeout=20)`
|
|
97
|
+
|
|
98
|
+
手动建立与手机 WebView 的调试连接。成功后返回 `DrissionPage.Chromium` 对象。
|
|
99
|
+
|
|
100
|
+
### `webview.current_page` (Property)
|
|
101
|
+
|
|
102
|
+
**核心属性**。获取当前活跃的标签页对象(`ChromiumTab`)。
|
|
103
|
+
|
|
104
|
+
- *注:若未连接,访问此属性将自动调用 `attach()`。*
|
|
105
|
+
|
|
106
|
+
### `webview.detach()`
|
|
107
|
+
|
|
108
|
+
断开 CDP 连接并移除 ADB 端口转发映射,释放系统资源。
|
|
109
|
+
|
|
110
|
+
## 常见问题
|
|
111
|
+
|
|
112
|
+
**Q: 为什么找不到 WebView Socket?**
|
|
113
|
+
|
|
114
|
+
1. 请确认 App 已经进入了包含 H5 的 Activity。
|
|
115
|
+
2. 请确认 App 源码中开启了 WebView 调试:`WebView.setWebContentsDebuggingEnabled(true);`。如果是第三方 App,可能需要使用 Xposed 模块(如 WebViewDebugHook)强制开启。
|
|
116
|
+
|
|
117
|
+
**Q: 是否支持多设备并行?** 支持。每个 `Webview` 实例在初始化时都会自动分配一个独立的本地空闲端口,多台手机同时运行不会发生冲突。
|
|
118
|
+
|
|
119
|
+
## 开源协议
|
|
120
|
+
|
|
121
|
+
本项目采用 [MIT License](https://opensource.org/licenses/MIT) 协议。
|
|
122
|
+
|
|
123
|
+
**贡献与支持**: 欢迎提交 Issue 或 Pull Request 来完善本项目。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 文件:setup.py
|
|
2
|
+
from setuptools import setup, find_packages
|
|
3
|
+
|
|
4
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
5
|
+
long_description = fh.read()
|
|
6
|
+
|
|
7
|
+
setup(
|
|
8
|
+
name="u2_webview",
|
|
9
|
+
version="0.1.0",
|
|
10
|
+
description="An extension for uiautomator2 to support WebView automation via DrissionPage",
|
|
11
|
+
author="YuYoungG",
|
|
12
|
+
author_email="younggg2218@gmail.com",
|
|
13
|
+
packages=find_packages(),
|
|
14
|
+
long_description=long_description,
|
|
15
|
+
long_description_content_type="text/markdown",
|
|
16
|
+
url="https://github.com/YuYoungG/uiautomator2-webview",
|
|
17
|
+
install_requires=[ # 依赖的其他库
|
|
18
|
+
"uiautomator2>=2.16.0",
|
|
19
|
+
"DrissionPage>=4.1.0",
|
|
20
|
+
"adbutils>=0.11.0"
|
|
21
|
+
],
|
|
22
|
+
classifiers=[
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
],
|
|
27
|
+
python_requires=">=3.8",
|
|
28
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# -*- coding:utf-8 -*-
|
|
2
|
+
import time
|
|
3
|
+
import re
|
|
4
|
+
import socket
|
|
5
|
+
import atexit
|
|
6
|
+
import traceback
|
|
7
|
+
from adbutils import adb
|
|
8
|
+
from DrissionPage import Chromium, ChromiumOptions
|
|
9
|
+
|
|
10
|
+
class Webview:
|
|
11
|
+
"""
|
|
12
|
+
u2_webview 扩展类
|
|
13
|
+
设计参考 Flask Extension 模式:支持预初始化和延迟绑定设备
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, d=None):
|
|
16
|
+
self.d = None
|
|
17
|
+
self.browser = None
|
|
18
|
+
self.local_port = None
|
|
19
|
+
self._socket_name = None
|
|
20
|
+
|
|
21
|
+
# 如果初始化时传入了设备,则直接绑定
|
|
22
|
+
if d is not None:
|
|
23
|
+
self.init_device(d)
|
|
24
|
+
|
|
25
|
+
def init_device(self, d):
|
|
26
|
+
"""
|
|
27
|
+
将扩展绑定到具体的 uiautomator2 Device 实例上
|
|
28
|
+
"""
|
|
29
|
+
self.d = d
|
|
30
|
+
# 自动获取一个当前实例专用的空闲端口
|
|
31
|
+
self.local_port = self._get_free_port()
|
|
32
|
+
|
|
33
|
+
# 注册进程退出时的清理
|
|
34
|
+
atexit.register(self.detach)
|
|
35
|
+
|
|
36
|
+
def _get_free_port(self):
|
|
37
|
+
"""获取本地空闲端口"""
|
|
38
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
39
|
+
s.bind(('localhost', 0))
|
|
40
|
+
return s.getsockname()[1]
|
|
41
|
+
|
|
42
|
+
def _find_webview_socket(self, timeout=15):
|
|
43
|
+
"""侦测手机内 WebView 调试接口"""
|
|
44
|
+
if not self.d:
|
|
45
|
+
raise RuntimeError("扩展尚未绑定设备,请先调用 init_device(d)")
|
|
46
|
+
|
|
47
|
+
start_time = time.time()
|
|
48
|
+
while time.time() - start_time < timeout:
|
|
49
|
+
try:
|
|
50
|
+
# grep -a 处理二进制干扰
|
|
51
|
+
output = self.d.shell("cat /proc/net/unix | grep -a devtools_remote").output.strip()
|
|
52
|
+
if output:
|
|
53
|
+
lines = output.splitlines()
|
|
54
|
+
for line in reversed(lines):
|
|
55
|
+
match = re.search(r'webview_devtools_remote_\d+', line) or \
|
|
56
|
+
re.search(r'chrome_devtools_remote_\d+', line)
|
|
57
|
+
if match:
|
|
58
|
+
return match.group(0)
|
|
59
|
+
except Exception:
|
|
60
|
+
traceback.print_exc()
|
|
61
|
+
time.sleep(0.5)
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def attach(self, timeout=20):
|
|
65
|
+
"""建立连接(核心行为)"""
|
|
66
|
+
if self.browser:
|
|
67
|
+
return self.browser
|
|
68
|
+
|
|
69
|
+
if not self.d:
|
|
70
|
+
raise RuntimeError("请先通过 Webview(d) 或 init_device(d) 绑定设备")
|
|
71
|
+
|
|
72
|
+
socket_name = self._find_webview_socket(timeout=timeout)
|
|
73
|
+
if not socket_name:
|
|
74
|
+
raise RuntimeError("❌ 未检测到 WebView Socket,请确认 App 已进入 H5 页面并开启调试")
|
|
75
|
+
|
|
76
|
+
# 建立 ADB 隧道
|
|
77
|
+
try:
|
|
78
|
+
device = adb.device(serial=self.d.serial)
|
|
79
|
+
device.forward(f"tcp:{self.local_port}", f"localabstract:{socket_name}")
|
|
80
|
+
except Exception:
|
|
81
|
+
traceback.print_exc()
|
|
82
|
+
|
|
83
|
+
# 连接 DrissionPage
|
|
84
|
+
try:
|
|
85
|
+
co = ChromiumOptions()
|
|
86
|
+
co.set_address(f'127.0.0.1:{self.local_port}')
|
|
87
|
+
self.browser = Chromium(addr_or_opts=co)
|
|
88
|
+
return self.browser
|
|
89
|
+
except Exception:
|
|
90
|
+
traceback.print_exc()
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def current_page(self):
|
|
94
|
+
"""
|
|
95
|
+
懒加载获取页面。
|
|
96
|
+
如果在未 attach 的情况下访问该属性,会自动触发 attach 逻辑。
|
|
97
|
+
"""
|
|
98
|
+
if not self.browser:
|
|
99
|
+
self.attach()
|
|
100
|
+
return self.browser.latest_tab
|
|
101
|
+
|
|
102
|
+
def detach(self):
|
|
103
|
+
"""断开链接清理资源"""
|
|
104
|
+
if self.browser:
|
|
105
|
+
self.browser = None
|
|
106
|
+
if self.d and self.local_port:
|
|
107
|
+
try:
|
|
108
|
+
device = adb.device(serial=self.d.serial)
|
|
109
|
+
device.forward_remove(f"tcp:{self.local_port}")
|
|
110
|
+
except Exception:
|
|
111
|
+
traceback.print_exc()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: u2_webview
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An extension for uiautomator2 to support WebView automation via DrissionPage
|
|
5
|
+
Home-page: https://github.com/YuYoungG/uiautomator2-webview
|
|
6
|
+
Author: YuYoungG
|
|
7
|
+
Author-email: younggg2218@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: uiautomator2>=2.16.0
|
|
15
|
+
Requires-Dist: DrissionPage>=4.1.0
|
|
16
|
+
Requires-Dist: adbutils>=0.11.0
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
Dynamic: summary
|
|
27
|
+
|
|
28
|
+
# u2_webview
|
|
29
|
+
|
|
30
|
+
**u2_webview** 是一个专为 `uiautomator2` 定制的混合应用(Hybrid App)自动化扩展库。它通过集成 `DrissionPage` ,实现了对移动端 WebView 的“无驱动”(Driverless)接管。
|
|
31
|
+
|
|
32
|
+
## 核心优势
|
|
33
|
+
|
|
34
|
+
- **免驱动接管 (Driverless)**:不同于传统的 Selenium/Appium,本库无需下载、配置或匹配特定版本的 `chromedriver`。它通过 CDP 协议直接与 WebView 通信,彻底告别驱动版本不匹配的烦恼。
|
|
35
|
+
- **Flask 扩展模式设计**:遵循 Flask 插件设计哲学,支持“应用工厂”模式,实现与 `uiautomator2.Device` 实例的完全解耦。
|
|
36
|
+
- **高性能通信**:基于 `adbutils` 建立高效的端口转发隧道,确保 H5 操作的高响应速度与稳定性。
|
|
37
|
+
- **API 极简**:只需一个属性 `.current_page`,即可像操作浏览器一样操作手机内的 H5 页面。
|
|
38
|
+
|
|
39
|
+
## 环境要求
|
|
40
|
+
|
|
41
|
+
- **Python**: 3.8 或更高版本
|
|
42
|
+
- **Android 设备**: 需开启 ADB 调试
|
|
43
|
+
- **被测 App**: WebView 必须开启调试模式(`setWebContentsDebuggingEnabled(true)`)
|
|
44
|
+
|
|
45
|
+
## 安装
|
|
46
|
+
|
|
47
|
+
通过 PyPI 直接安装:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install u2_webview
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
或者从源码本地安装(开发模式):
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
git clone [https://github.com/YuYoungG/uiautomator2-webview.git](https://github.com/YuYoungG/uiautomator2-webview.git)
|
|
57
|
+
cd uiautomator2-webview
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 使用指南
|
|
62
|
+
|
|
63
|
+
本库支持两种初始化模式,以适配不同的框架架构。
|
|
64
|
+
|
|
65
|
+
### 1. 基础用法 (直接绑定)
|
|
66
|
+
|
|
67
|
+
适用于简单的脚本测试。
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
import uiautomator2 as u2
|
|
71
|
+
from u2_webview import Webview
|
|
72
|
+
|
|
73
|
+
# 连接设备
|
|
74
|
+
d = u2.connect()
|
|
75
|
+
|
|
76
|
+
# 实例化扩展并绑定设备
|
|
77
|
+
webview = Webview(d)
|
|
78
|
+
|
|
79
|
+
# 访问 H5 页面属性 (会自动触发 attach)
|
|
80
|
+
print(f"当前 H5 标题: {webview.current_page.title}")
|
|
81
|
+
|
|
82
|
+
# 使用 DrissionPage 语法进行操作
|
|
83
|
+
webview.current_page.ele('text:登录').click()
|
|
84
|
+
|
|
85
|
+
# 测试结束,清理资源
|
|
86
|
+
webview.detach()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. 工厂模式用法 (推荐用于大型框架)
|
|
90
|
+
|
|
91
|
+
类似于 Flask 的 `init_app` 模式,适合在设备对象尚未完全确定时预定义扩展。
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
from u2_webview import Webview
|
|
95
|
+
import uiautomator2 as u2
|
|
96
|
+
|
|
97
|
+
# 全局预定义扩展对象
|
|
98
|
+
webview = Webview()
|
|
99
|
+
|
|
100
|
+
def run_test(serial):
|
|
101
|
+
d = u2.connect(serial)
|
|
102
|
+
|
|
103
|
+
# 在运行时绑定具体设备
|
|
104
|
+
webview.init_device(d)
|
|
105
|
+
|
|
106
|
+
# 接管并操作
|
|
107
|
+
page = webview.current_page
|
|
108
|
+
page.actions.move_to('.slider').click()
|
|
109
|
+
|
|
110
|
+
webview.detach()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 核心 API 参考
|
|
114
|
+
|
|
115
|
+
### `Webview(d=None)`
|
|
116
|
+
|
|
117
|
+
构造函数。可选参数 `d` 为 `uiautomator2.connect()` 返回的对象。
|
|
118
|
+
|
|
119
|
+
### `webview.init_device(d)`
|
|
120
|
+
|
|
121
|
+
将扩展实例绑定到特定的 `uiautomator2` 设备对象。
|
|
122
|
+
|
|
123
|
+
### `webview.attach(timeout=20)`
|
|
124
|
+
|
|
125
|
+
手动建立与手机 WebView 的调试连接。成功后返回 `DrissionPage.Chromium` 对象。
|
|
126
|
+
|
|
127
|
+
### `webview.current_page` (Property)
|
|
128
|
+
|
|
129
|
+
**核心属性**。获取当前活跃的标签页对象(`ChromiumTab`)。
|
|
130
|
+
|
|
131
|
+
- *注:若未连接,访问此属性将自动调用 `attach()`。*
|
|
132
|
+
|
|
133
|
+
### `webview.detach()`
|
|
134
|
+
|
|
135
|
+
断开 CDP 连接并移除 ADB 端口转发映射,释放系统资源。
|
|
136
|
+
|
|
137
|
+
## 常见问题
|
|
138
|
+
|
|
139
|
+
**Q: 为什么找不到 WebView Socket?**
|
|
140
|
+
|
|
141
|
+
1. 请确认 App 已经进入了包含 H5 的 Activity。
|
|
142
|
+
2. 请确认 App 源码中开启了 WebView 调试:`WebView.setWebContentsDebuggingEnabled(true);`。如果是第三方 App,可能需要使用 Xposed 模块(如 WebViewDebugHook)强制开启。
|
|
143
|
+
|
|
144
|
+
**Q: 是否支持多设备并行?** 支持。每个 `Webview` 实例在初始化时都会自动分配一个独立的本地空闲端口,多台手机同时运行不会发生冲突。
|
|
145
|
+
|
|
146
|
+
## 开源协议
|
|
147
|
+
|
|
148
|
+
本项目采用 [MIT License](https://opensource.org/licenses/MIT) 协议。
|
|
149
|
+
|
|
150
|
+
**贡献与支持**: 欢迎提交 Issue 或 Pull Request 来完善本项目。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
u2_webview
|