uiautomator2自动化

uiautomator2自动化

windows+adb

Soul合成大西瓜,横坐标随机,纵坐标固定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# coding: utf-8
import uiautomator2 as u2
import random
import time

d = u2.connect()
i = 0
max_cnt = 100

while i < max_cnt:
    sel = d(resourceId="cn.soulapp.android:id/enterMsgRootGiftTag")
    # 等待元素出现,最多8秒
    if not sel.wait(timeout=8):
        print("? 未找到目标元素,跳过本次")
        time.sleep(1)
        continue

    # 获取控件边界 bounds
    b = sel.info["bounds"]
    left, top, right, bottom = b["left"], b["top"], b["right"], b["bottom"]
    w = right - left
    h = bottom - top

    # 在控件内部:x相对0.5~0.9,y固定0.354
    px = left + w * random.uniform(0.5, 0.9)
    py = top + h * 0.354
    print(f"点击像素坐标: ({px:.0f}, {py:.0f})")

    d.click(px, py)
    time.sleep(random.uniform(0.4, 1.2))
    i += 1
参考链接

https://www.doubao.com/thread/xFMig7NpgesDLo8ai

Termux+adb+uiautomator2

termux+uiautomator2滑动屏幕

termux+uiautomator2滑动屏幕
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys
import uiautomator2 as u2
import time

d = u2.connect(sys.argv[1])

# 滑动
# d.swipe(0, 692, 0, 0, duration=1)
for i in range(10):  # 滑动10次
    # 方法1:直接滑动
    d.swipe(253, 984, 253, 752, duration=1)

    # # 方法2:尝试通过元素滑动(更推荐)
    # scrollable = d.xpath('//*[@scrollable="true"]').get()
    # if scrollable:
    #     scrollable.swipe("up")

    time.sleep(1.5)  # 滑动间隔
    print(f"已完成第 {i+1} 次滑动")

参数duration数值可调,也可去除,不过建议保留这个参数

termux+uiautomator2给多个QQ好友点赞
安装依赖和uiautomator2库并通过打印设备详情测试环境
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  # 安装依赖
  apt install -y clang libxml2 libxslt python-pip libjpeg-turbo zlib android-tools 
  pip install cython lxml
  # 安装库
  pip install --pre uiautomator2
  # 初始化设备  
  # 手机无线调试并执行adb配对,然后连接手机ip对应的adb设备,端口为5555是因为配置连接后执行 adb tcpip 5555 设置的
  adb connect 192.168.1.101:5555
  {
	echo 'import uiautomator2 as u2'
	echo 'd = u2.connect("192.168.1.101")'
	echo 'print (d.info)'
  } > testdemo.py
  python -m uiautomator2 init		# 前提是USB调试打开的情况下已经连接无线调试或者USB连接电脑
  python testdemo.py

最后执行testdemo.py打印信息如下:

{'currentPackageName': 'com.tencent.mobileqq', 'displayHeight': 2400, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 914, 'displayWidth': 1080, 'productName': 'CN_AI2401', 'screenOn': True, 'sdkInt': 35, 'naturalOrientation': True}

环境没有问题了之后就可以进行下一步了,借助WEditor写QQ好友点赞脚本
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys
import uiautomator2 as u2

d = u2.connect('192.168.1.101')

# 可以通过传入ip参数执行该程序,如:python qq好友点赞.py 192.168.1.101
# d = u2.connect(sys.argv[1])

d.app_start('com.tencent.mobileqq')
d.xpath('//*[@resource-id="com.tencent.mobileqq:id/e3u"]/android.widget.FrameLayout[1]').click()
d(resourceId="com.tencent.mobileqq:id/us4").click()
d.click(949,650)
for i in range(1):
  for j in range(10):
    xpath = f'//*[@resource-id="com.tencent.mobileqq:id/kzn"]/android.widget.LinearLayout[{i+1}]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]'
    d.xpath(xpath).click()

d.app_stop('com.tencent.mobileqq')
# d.app_clear('com.tencent.mobileqq')

在WEditor中连接adb设备之后,通过不断的不断的双击控件和刷新界面,进而不断的更新控件操作脚本,然后再根据规律将点选的XPath中嵌入for循环变量,来实现给多个好友点赞,变量i对应多个好友,一共给6个好友点赞,通过{i+1}来切换好友,{i+1}是因为变量是从0开始的,而第一个好友对应的变量是从1开始的,所以变量i加1,然后每个好友通过变量j循环点赞10次。如果只要给第1个好友点赞,可以直接修改循环变量i的范围由6改为1,或者修改{i+1}为固定值比如3或者5,由于是修改为固定值,此时没有变量j的参与,所以把花括号也去掉。

脚本优化-1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import os
import time
import uiautomator2 as u2

connect=os.system('adb devices | grep -w "device" | awk \'{print $1}\'')
d = u2.connect(connect)
# d = u2.connect('127.0.0.1')
# 也可以通过传入ip参数执行该程序,如:python qq好友点赞.py 192.168.1.101
# d = u2.connect(sys.argv[1])

# d.app_stop('com.tencent.mobileqq') # 如果是调试时可以取消注释该行,该行代码作用是为了每次打开指定软件前先关闭指定软件
d.app_start('com.tencent.mobileqq')
d.xpath('//*[@resource-id="com.tencent.mobileqq:id/e3u"]/android.widget.FrameLayout[1]').click()
d(resourceId="com.tencent.mobileqq:id/uu4").click()

d(resourceId="com.tencent.mobileqq:id/l0g").click()
time.sleep(0.5)

if d(resourceId="com.tencent.mobileqq:id/h7r").exists:
  d(resourceId="com.tencent.mobileqq:id/h7s").click()
  time.sleep(0.3)
else:
  for i in range(1):
    xpath = f'//*[@resource-id="com.tencent.mobileqq:id/kzn"]/android.widget.LinearLayout[{i+1}]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]'
    for j in range(10):
      d.xpath(xpath).click()
    time.sleep(0.3)

d.app_stop('com.tencent.mobileqq')

最后添加了d.app_stop(),qq点完赞后会自动关闭。

执行方式
termux+uiautomator2自动化
termux环境设置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apt install -y clang libxml2 libxslt python-pip libjpeg-turbo zlib android-tools 
pip install cython lxml
pip install --pre uiautomator2
python -m uiautomator2 init		# 前提是USB调试打开的情况下已经连接无线调试或者USB连接电脑
adb connect 192.168.3.175:5555
{
  echo 'import uiautomator2 as u2'
  echo 'dev = u2.connect("192.168.3.175")'
  echo 'print (dev.info)'
} > testdemo.py
python testdemo.py
给QQ好友点赞
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import uiautomator2 as u2

def main():
    u = u2.connect('192.168.3.193')    
    
    # 输入设备
    # print(u.info)

    # QQ好友点赞
    u.app_start('com.tencent.mobileqq')    
    u.click(88,173)
    u(text='夜闹').click()
    u.click(975,695)
    for i in range(10):
        u.click(1018,1157)

if __name__ == '__main__':
    main()
元素定位
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import uiautomator2 as u2

def main():
    u = u2.connect('192.168.3.193')    
    
    # 元素定位
    xml = u.dump_hierarchy()
    # print(xml)
    # 保存到文件(方便分析)
    with open("ui_dump.xml", "w", encoding="utf-8") as f:
        f.write(xml)

if __name__ == '__main__':
    main()
使用 weditor 可视化查看元素
1
2
3
4
# pip install weditor setuptools
pip install setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -e weditor -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m weditor

浏览器会自动打开 http://localhost:17310,选择设备后即可 实时查看界面元素(类似 Appium Inspector)。

python+uiautomator2实现termux的数据清除与初始化配置,待完善
执行方式
1
python cfg.py
安装依赖
1
pip3 install pywinauto
cfg.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 打开镜像选择界面
termux-change-repo

pkg update
pkg install -y termux-auth expect android-tools openssh

expect <<'EOF'
spawn passwd
expect "New password:" { send "..Qq1ssh..\r" }
expect "Retype new password:" { send "..Qq1ssh..\r" }
expect eof
EOF
cfg.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# coding: utf-8
#
import uiautomator2 as u2
import subprocess
import os
import time

# 首先确保手机与电脑通过USB连接或者电脑通过ip连接手机adb,如:adb connect 192.168.3.178:5555
result = subprocess.check_output(['adb', 'devices']).decode('utf-8')
devices = [line.split('\t')[0]
           for line in result.splitlines()
           if '\tdevice' in line]
connect = devices[0]
d = u2.connect(connect)

# 清除app的数据
d.app_clear('com.termux')

# 直接通过包名打开app
d.app_start('com.termux')
time.sleep(1)
d(resourceId="com.termux:id/drawer_layout").click()

# 获取存储访问的权限
os.system(f'adb -s {connect} shell input text "termux-setup-storage"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 发送回车键
# 弹窗请求允许,点击允许
d(resourceId="com.android.permissioncontroller:id/permission_allow_button").click()

# 拷贝脚本
os.system(f'adb -s {connect} push cfg.sh /storage/self/primary/Download/'),
time.sleep(1)
cmd = "cp -a storage\/shared\/Download\/cfg.sh ~/".replace(" ", "%s")
os.system(f'adb -s {connect} shell input text "{cmd}"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 回车

# 运行脚本
cmd = "bash ~/cfg.sh".replace(" ", "%s")
os.system(f'adb -s {connect} shell input text "{cmd}"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 回车

# 换源
# 模拟按键操作(需根据实际UI调整延迟)
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_SPACE')  # 全选
# time.sleep(2)
d.shell('input keyevent KEYCODE_ENTER')  # 确认
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_DPAD_DOWN 2')  # 选择中国镜像(按2次方向键下)
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_ENTER')  # 确认
time.sleep(30)
uiautomator2自动化
windows+adb
Soul合成大西瓜,横坐标随机,纵坐标固定
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# coding: utf-8
import uiautomator2 as u2
import random
import time

d = u2.connect()
i = 0
max_cnt = 100

while i < max_cnt:
    sel = d(resourceId="cn.soulapp.android:id/enterMsgRootGiftTag")
    # 等待元素出现,最多8秒
    if not sel.wait(timeout=8):
        print("? 未找到目标元素,跳过本次")
        time.sleep(1)
        continue

    # 获取控件边界 bounds
    b = sel.info["bounds"]
    left, top, right, bottom = b["left"], b["top"], b["right"], b["bottom"]
    w = right - left
    h = bottom - top

    # 在控件内部:x相对0.5~0.9,y固定0.354
    px = left + w * random.uniform(0.5, 0.9)
    py = top + h * 0.354
    print(f"点击像素坐标: ({px:.0f}, {py:.0f})")

    d.click(px, py)
    time.sleep(random.uniform(0.4, 1.2))
    i += 1
参考链接

https://www.doubao.com/thread/xFMig7NpgesDLo8ai

Termux
termux+uiautomator2滑动屏幕
termux+uiautomator2滑动屏幕
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys
import uiautomator2 as u2
import time

d = u2.connect(sys.argv[1])

# 滑动
# d.swipe(0, 692, 0, 0, duration=1)
for i in range(10):  # 滑动10次
    # 方法1:直接滑动
    d.swipe(253, 984, 253, 752, duration=1)

    # # 方法2:尝试通过元素滑动(更推荐)
    # scrollable = d.xpath('//*[@scrollable="true"]').get()
    # if scrollable:
    #     scrollable.swipe("up")

    time.sleep(1.5)  # 滑动间隔
    print(f"已完成第 {i+1} 次滑动")

参数duration数值可调,也可去除,不过建议保留这个参数

termux+uiautomator2给多个QQ好友点赞
安装依赖和uiautomator2库并通过打印设备详情测试环境
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  # 安装依赖
  apt install -y clang libxml2 libxslt python-pip libjpeg-turbo zlib android-tools 
  pip install cython lxml
  # 安装库
  pip install --pre uiautomator2
  # 初始化设备  
  # 手机无线调试并执行adb配对,然后连接手机ip对应的adb设备,端口为5555是因为配置连接后执行 adb tcpip 5555 设置的
  adb connect 192.168.1.101:5555
  {
	echo 'import uiautomator2 as u2'
	echo 'd = u2.connect("192.168.1.101")'
	echo 'print (d.info)'
  } > testdemo.py
  python -m uiautomator2 init		# 前提是USB调试打开的情况下已经连接无线调试或者USB连接电脑
  python testdemo.py

最后执行testdemo.py打印信息如下:

{'currentPackageName': 'com.tencent.mobileqq', 'displayHeight': 2400, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 914, 'displayWidth': 1080, 'productName': 'CN_AI2401', 'screenOn': True, 'sdkInt': 35, 'naturalOrientation': True}

环境没有问题了之后就可以进行下一步了,借助WEditor写QQ好友点赞脚本
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import sys
import uiautomator2 as u2

d = u2.connect('192.168.1.101')

# 可以通过传入ip参数执行该程序,如:python qq好友点赞.py 192.168.1.101
# d = u2.connect(sys.argv[1])

d.app_start('com.tencent.mobileqq')
d.xpath('//*[@resource-id="com.tencent.mobileqq:id/e3u"]/android.widget.FrameLayout[1]').click()
d(resourceId="com.tencent.mobileqq:id/us4").click()
d.click(949,650)
for i in range(1):
  for j in range(10):
    xpath = f'//*[@resource-id="com.tencent.mobileqq:id/kzn"]/android.widget.LinearLayout[{i+1}]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]'
    d.xpath(xpath).click()

d.app_stop('com.tencent.mobileqq')
# d.app_clear('com.tencent.mobileqq')

在WEditor中连接adb设备之后,通过不断的不断的双击控件和刷新界面,进而不断的更新控件操作脚本,然后再根据规律将点选的XPath中嵌入for循环变量,来实现给多个好友点赞,变量i对应多个好友,一共给6个好友点赞,通过{i+1}来切换好友,{i+1}是因为变量是从0开始的,而第一个好友对应的变量是从1开始的,所以变量i加1,然后每个好友通过变量j循环点赞10次。如果只要给第1个好友点赞,可以直接修改循环变量i的范围由6改为1,或者修改{i+1}为固定值比如3或者5,由于是修改为固定值,此时没有变量j的参与,所以把花括号也去掉。

脚本优化-1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import os
import time
import uiautomator2 as u2

connect=os.system('adb devices | grep -w "device" | awk \'{print $1}\'')
d = u2.connect(connect)
# d = u2.connect('127.0.0.1')
# 也可以通过传入ip参数执行该程序,如:python qq好友点赞.py 192.168.1.101
# d = u2.connect(sys.argv[1])

# d.app_stop('com.tencent.mobileqq') # 如果是调试时可以取消注释该行,该行代码作用是为了每次打开指定软件前先关闭指定软件
d.app_start('com.tencent.mobileqq')
d.xpath('//*[@resource-id="com.tencent.mobileqq:id/e3u"]/android.widget.FrameLayout[1]').click()
d(resourceId="com.tencent.mobileqq:id/uu4").click()

d(resourceId="com.tencent.mobileqq:id/l0g").click()
time.sleep(0.5)

if d(resourceId="com.tencent.mobileqq:id/h7r").exists:
  d(resourceId="com.tencent.mobileqq:id/h7s").click()
  time.sleep(0.3)
else:
  for i in range(1):
    xpath = f'//*[@resource-id="com.tencent.mobileqq:id/kzn"]/android.widget.LinearLayout[{i+1}]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView[1]'
    for j in range(10):
      d.xpath(xpath).click()
    time.sleep(0.3)

d.app_stop('com.tencent.mobileqq')

最后添加了d.app_stop(),qq点完赞后会自动关闭。

执行方式
termux+uiautomator2自动化
termux环境设置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apt install -y clang libxml2 libxslt python-pip libjpeg-turbo zlib android-tools 
pip install cython lxml
pip install --pre uiautomator2
python -m uiautomator2 init		# 前提是USB调试打开的情况下已经连接无线调试或者USB连接电脑
adb connect 192.168.3.175:5555
{
  echo 'import uiautomator2 as u2'
  echo 'dev = u2.connect("192.168.3.175")'
  echo 'print (dev.info)'
} > testdemo.py
python testdemo.py
给QQ好友点赞
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import uiautomator2 as u2

def main():
    u = u2.connect('192.168.3.193')    
    
    # 输入设备
    # print(u.info)

    # QQ好友点赞
    u.app_start('com.tencent.mobileqq')    
    u.click(88,173)
    u(text='夜闹').click()
    u.click(975,695)
    for i in range(10):
        u.click(1018,1157)

if __name__ == '__main__':
    main()
元素定位
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import uiautomator2 as u2

def main():
    u = u2.connect('192.168.3.193')    
    
    # 元素定位
    xml = u.dump_hierarchy()
    # print(xml)
    # 保存到文件(方便分析)
    with open("ui_dump.xml", "w", encoding="utf-8") as f:
        f.write(xml)

if __name__ == '__main__':
    main()
使用 weditor 可视化查看元素
1
2
3
4
# pip install weditor setuptools
pip install setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -e weditor -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m weditor

浏览器会自动打开 http://localhost:17310,选择设备后即可 实时查看界面元素(类似 Appium Inspector)。

python+uiautomator2实现termux的数据清除与初始化配置,待完善
执行方式
1
python cfg.py
安装依赖
1
pip3 install pywinauto
cfg.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 打开镜像选择界面
termux-change-repo

pkg update
pkg install -y termux-auth expect android-tools openssh

expect <<'EOF'
spawn passwd
expect "New password:" { send "..Qq1ssh..\r" }
expect "Retype new password:" { send "..Qq1ssh..\r" }
expect eof
EOF
cfg.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# coding: utf-8
#
import uiautomator2 as u2
import subprocess
import os
import time

# 首先确保手机与电脑通过USB连接或者电脑通过ip连接手机adb,如:adb connect 192.168.3.178:5555
result = subprocess.check_output(['adb', 'devices']).decode('utf-8')
devices = [line.split('\t')[0]
           for line in result.splitlines()
           if '\tdevice' in line]
connect = devices[0]
d = u2.connect(connect)

# 清除app的数据
d.app_clear('com.termux')

# 直接通过包名打开app
d.app_start('com.termux')
time.sleep(1)
d(resourceId="com.termux:id/drawer_layout").click()

# 获取存储访问的权限
os.system(f'adb -s {connect} shell input text "termux-setup-storage"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 发送回车键
# 弹窗请求允许,点击允许
d(resourceId="com.android.permissioncontroller:id/permission_allow_button").click()

# 拷贝脚本
os.system(f'adb -s {connect} push cfg.sh /storage/self/primary/Download/'),
time.sleep(1)
cmd = "cp -a storage\/shared\/Download\/cfg.sh ~/".replace(" ", "%s")
os.system(f'adb -s {connect} shell input text "{cmd}"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 回车

# 运行脚本
cmd = "bash ~/cfg.sh".replace(" ", "%s")
os.system(f'adb -s {connect} shell input text "{cmd}"')
os.system(f'adb -s {connect} shell input keyevent 66')  # 回车

# 换源
# 模拟按键操作(需根据实际UI调整延迟)
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_SPACE')  # 全选
# time.sleep(2)
d.shell('input keyevent KEYCODE_ENTER')  # 确认
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_DPAD_DOWN 2')  # 选择中国镜像(按2次方向键下)
# time.sleep(2)
os.system(f'adb -s {connect} shell input keyevent KEYCODE_ENTER')  # 确认
time.sleep(30)
termux+python+uiautomator2获取闹钟状态及闹钟状态设定
代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import os
import time
import uiautomator2 as u2

connect=os.system('adb devices | grep -w "device" | awk \'{print $1}\'')
d = u2.connect(connect)

d.app_stop('com.asus.deskclock')
d.app_start('com.asus.deskclock')

# 获取某元素下的所有属性
# flag=d.xpath('//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[4]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').info
# print(flag)

for alarm_index in range(1,5):
  # 获取某元素下的所有属性
  # flag=d.xpath(f'//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[{alarm_index}]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').info

  # 获取某元素下的指定属性
  flag=d.xpath(f'//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[{alarm_index}]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').info['checked']
  # 判断闹钟复选框是未勾选状态,则打开闹钟
  if flag==False:
    d.xpath(f'//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[{alarm_index}]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').click()

  # 判断闹钟复选框是勾选状态,则关闭闹钟
  else:
    d.xpath(f'//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[{alarm_index}]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').click()
    time.sleep(0.1)
    d(text="关闭这个重复闹钟").click()
    time.sleep(0.1)

time.sleep(1)
d.swipe(28,1370,28,0,duration=0.7)
for alarm_index in range(1,9):
  flag=d.xpath(f'//*[@resource-id="com.asus.deskclock:id/alarms_list"]/android.widget.LinearLayout[{alarm_index}]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Switch[1]').info['checked']
  print(flag)
python+uiautomator2实现soul循环自动发送指定文本
环境要求

支持设备:安卓

手机要求:支持无线调试

运行环境:Termux

Termux环境搭建
1
2
3
pkg install -y libxml2 libxslt
pkg install libjpeg-turbo libtiff libwebp openjpeg
pip install uiautomator2
Windows搭建环境

自行查找相关教程安装anaconda,版本最新即可,这里不过多赘述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 创建一个名为uiautomator2的指定版本的环境,-y选项自动安装环境依赖
conda create -n uiautomator2 python=3.8 -y
# 激活环境
conda activate uiautomator2

python -m pip install --pre uiautomator2 -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m pip install keyboard -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m uiautomator2 init		# 前提是USB调试打开的情况下已经连接无线调试或者USB连接电脑
git -c http.proxy="127.0.0.1:10808" clone https://github.com/openatx/weditor
python -m pip install -e weditor -i https://pypi.tuna.tsinghua.edu.cn/simple
功能

在soul派对界面发送指定文本到公屏。

代码
版本1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# coding: utf-8
#
import os
import uiautomator2 as u2
import keyboard
import time

connect=os.system('adb devices | grep -w "device" | awk \'NR==1{print $1}\'')
d = u2.connect(connect)

while True:
    time.sleep(1);
    d(resourceId="cn.soulapp.android:id/tvChat").click()
    time.sleep(1);
    d.send_keys("加入我们单身社交交友大家庭\n包脱单,包分配,包售后,包打假\n看群主瞬间小猪猪蹄下边的水槽那一串黑黑的摩斯密码,备注soul名字,拉你进大家庭~", clear=True)
    time.sleep(1);
    d(resourceId="cn.soulapp.android:id/btnSend").click()
    d.click(0.402,0.745)
    time.sleep(60);
版本2
运行命令
1
2
3
4
conda activate uiautomator2
while true; do python soul自动发送.py; done
# 或
while true; do python soul自动发送.py device; done

device: 对应无线调试设备或USB设备

代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# coding: utf-8
#
import os
import sys
import time
import subprocess
import keyboard
import uiautomator2 as u2

def get_device_id():
    # 获取第第一个已连接的Android设备ID
    try:
        result = subprocess.check_output(['adb', 'devices']).decode('utf-8')
        devices = [line.split('\t')[0]
            for line in result.splitlines()
            if '\tdevice' in line]
        return devices[0] if devices else None
    except (subprocess.CalledProcessError, IndexError):
        return None

def main():
    running = True
    
    # print(len(sys.argv))
    if len(sys.argv) > 1:        
        device_id = sys.argv[1]
        if not device_id:
            print("Error: No connect Android device found")
            sys.exit(1)

    else:
        device_id = get_device_id()
        if not device_id:
            print("Error: No connect Android device found")
            sys.exit(1)

    print(f"Connected device: {device_id}")

    # 初始化uiautomator2连接
    try:
        d = u2.connect(device_id)
        print("Device connected successfully")

        while running:
            d(resourceId="cn.soulapp.android:id/tvChat").click()
            time.sleep(1);        
            # d.send_keys("加入我们单身社交交友大家庭\n包脱单,包分配,包售后,包打假\n看群主引力签,备注soul名字,拉你进大家庭~\n\n\n我们这个聚会,是包含吃火锅,可以喝酒,里边有KTV、台球、麻将,然后有社牛主持人带着大家做很多个破冰小游戏,互相了解,互相认识。就是你能够认识很多的朋友和异性,然后运气好的话,可以脱单儿。", clear=True)
            d.send_keys("欢迎加入泡泡同城单身交友大家庭\n包脱单,包分配,包售后\n看管理瞬间小猪猪屁股那一串白色的摩斯密码,备注soul名字,拉你进大家庭~\n\n\n今晚周六有个轰趴聚会,心动恋爱小游戏,火锅酒水一应俱全,小组破冰嗨起来,经典真心话大冒险,k歌桌游通通拿下。场地含KTV、台球、麻将,然后有社牛主持人带队,互相了解,互相认识。一次认识20➕同城单身朋友,欢迎加入~\n\n\n注意:不要只是关注群主,关注是没用的,公屏有提示", clear=True)
            # d.send_keys("test", clear=True)
            time.sleep(1);
            d(resourceId="cn.soulapp.android:id/btnSend").click()
            time.sleep(1);
            d.click(0.402,0.745)
            time.sleep(57);

    except Exception as e:
        print(f"Connected failed: {str(e)}")
        sys.exit(1)



if __name__ == "__main__":
    main()
termux+python+uiautomator2实现soul无限循环检测指定成员并对其进行封麦+禁言处理
代码实现

只需要修改要匹配的用户名字就行,名字由双引号括着:if d(descriptionContains="行一+一").exists() or d(textContains="行一+一").exists():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os
import re
import time
import uiautomator2 as u2

connect=os.system('adb devices | grep -w "device" | awk \'{print $1}\'')
d = u2.connect(connect)

element = d(resourceId="cn.soulapp.android:id/tvUserCount")
if element.exists():
    text = element.get_text()  # 获取元素文本,如 "粉丝数:12345"
    # print(text)
    numbers=re.findall(r'\d+', text)
    if numbers:
        number = int(numbers[0])  # 取第一个匹配项
        # print("提取到的数字:", number)
    else:
        print("文本中无数字")
else:
    print("未找到 tvUserCount 元素")    

while True:
    for i in range(number-1):
        xpath = f'//*[@resource-id="cn.soulapp.android:id/rvUser"]/android.widget.FrameLayout[{i+1}]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.widget.ImageView[1]'          
        d.xpath(xpath).click()        
        if d(descriptionContains="行一+一").exists() or d(textContains="行一+一").exists():
            print("文本存在(模糊匹配)")
            if d(text="封麦").exists:
                d(text="封麦").click()
            if d(text="取消封麦").exists:
                d(text="取消封麦").click()
                
            # time.sleep(1)            
            d(resourceId="cn.soulapp.android:id/iv_user_more").click()
            time.sleep(1)
            
            if d(text="禁止发言").exists:
                d(text="禁止发言").click()
            if d(text="取消禁言").exists:
                d(text="取消禁言").click()
        else:
            print("文本不存在")        
            d.press("back")
bash脚本+python+uiautomator2实现手机自动化配置
前言

须知 :该项目执行的手机切勿黑屏,不然安装软件会报异常导致软件不能正常安装。

该项目基本实现完全自动化,除了订阅链接的配置,之所以没有也根据自动化的方式实现,是考虑到用户根据自身需要可以手动添加根软件真正匹配的订阅链接,因为不是有的订阅链接不是很通用,这样做可以为了避免出问题。

另外该项目中某些软件的下载链接可能国内不能顺利访问或速度很慢,如果遇到这样的情况,可以电脑安装clash-verge科学上网软件,电脑通过该软件实现科学上网后,将该软件的TUN(在软件的设置界面中)打开,这样就可以实现终端也能访问外网了,这样在终端执行该项目会更方便一些。

源代码:python+uiautomator2实现手机自动化配置.zip

环境配置
  1. windows安装好python3的版本
  2. 安装git-bash
  3. 安装配置好Cygwin,在Cygwin中要安装常用工具如 curl、wget、jq
  4. 如果要配置科学上网软件的订阅链接的话,首先修改
  5. 科学上网软件如果更换了,同时需要通过该项目配置科学上网软件的订阅链接,可以查看自己的科学上网软件的包名,将实际的包名替换进该项目中,该项目配置了两个科学上网软件订阅链接,如果只需要配置一个科学上网软件,可以将另外一个注释掉。另外项目中两个软件的界面操作是根据软件界面的实际控件进行操作的,如果更换其他软件了,请根据软件的控制进行修改。
  6. 如果需要知道查看包名的方法的话,请按照如下方式操作:
1
2
3
4
# 根据关键词 v2ray 匹配科学上网软件v2ranNG的包名
adb -s aefcb1f3 shell pm list packages | grep "v2ray" | awk -F ':' '{print $2}'
# 同时查看软件软件对应的包名
adb -s aefcb1f3 shell pm list packages | grep -e "v2ray" -e "clash" | awk -F ':' '{print $2}'
  1. 在执行该项目前,需要基本确保环境配置好了,如果实际执行提示缺少什么库,可以根据终端提示的打印信息,再借助Deepseek完成环境的配置。
1
2
3
pip install uiautomator2
pip install keyboard
adb devices # 确认设备已连接
执行方式

在git-bash中运行该项目,cfg.sh与cfg.py和cfg.json要在同一目录下

根据以下执行方式运动该项目后,bash脚本会自动下载软件并安装,同时python脚本会根据手机界面的提示是否继续安装来作出反应,如果要配置科学上网软件的订阅链接的话,按字母 p 键(可长按,避免按一下时间太短导致脚本检测不到),当订阅链接配置完成后,根据提示按 q 键或按Ctrl-c结束该项目即可。

1
2
3
4
bash cfg.sh &                   # 第一步,先以后台执行的方式执行bash脚本,当所有程序安装完后自动结束该脚本
python cfg.py                   # 第二步,运行python脚本,循环检测,直到所有程序安装完后手动暂停该脚本
# 或
bash cfg.sh & \python cfg.py    # 跟前边的执行方式一样,不过这个执行方式是通过一条命令完成
cfg.json

.android.url对应的值对应软件下载链接,如果链接运行直接就能下载文件的话,则直接将下载链接添加到json文件中,如果是类似于api.github.com/repos这样格式的链接,返回的是json格式的数据,需要从中提取出需要下载的文件的下载链接,将提取表达式添加到json中即可。

github的文件之所以没有下载指定版本,是为了通过对api.github.com/repos内容中提出最新版本的文件。

airport_name 对应科学上网的机场,如果需要用到该键值的话,可以将 airport subscribed links 替换为自己真实的订阅链接,然后通过修改代码添加到科学上网软件中。同时安装好科学上网软件,如果安装的科学上网软件更换了,记得将python代码中的对应科学上网软件的包名也给替换成实际的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "android":{
        "url":[            
            {"lawnchair":"$(curl -s https://api.github.com/repos/LawnchairLauncher/lawnchair/releases/latest | jq -r '.assets[]' | jq -r '.browser_download_url' | grep 'Lawnchair')"},
            {"via":"https://res.viayoo.com/v1/via-release-cn.apk"},
            {"termux":"https://f-droid.org/repo/com.termux_1022.apk"},
            {"clash-meta":"$(curl -s https://api.github.com/repos/MetaCubeX/ClashMetaForAndroid/releases/latest | jq -r '.assets[]' | jq -r '.browser_download_url' | grep 'arm64')"},
            {"v2rayng":"$(curl -s https://api.github.com/repos/2dust/v2rayNG/releases/latest | jq -r '.assets[]' | jq -r '.browser_download_url' | grep 'arm64')"}
        ],
        "value":[
            {"lawnchair":"y"},
            {"via":"y"},
            {"termux":"y"},
            {"clash-meta":"y"},
            {"v2rayng":"y"}
        ]
    },
    "airport":{
        "airport_name":"https://example.AirportSubscribedLinks.test"
    },
    "function":[
        {"download_application":"y"},
        {"install_application":"y"},
        {"get_subscription":"y"}
    ]
}
cfg.sh

根据json文件中要下载文件对应的value判断是否下载,如果value值为y,则下载对应文件,否则跳过

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/bash

# 定义总文件数组
apk_array=($(jq -r '.android.value[] | keys[]' cfg.json))
# printf '%s\n' "${apk_array[@]}"

# 定义要安装文件的数组
apk_files=()

# 两种文件下载方式:
# 方式一
# # 下载安装文件,如果文件已存在则跳过
# download_application() {  
#   while IFS= read -r url; do
#     if [[ "$url" == \$\(* ]]; then url=$(eval echo "$url"); fi    
#     apk_files+=($(basename "$url"))
#     if [[ ! -f $(basename "$url") ]]; then wget --no-check-certificate "$url"; fi
#   done < <(jq -r '.android.url[].[]' cfg.json)
# }
# 方式二
# 根据json文件中要下载文件对应的value判断是否下载,如果value值为y,则下载对应文件,否则跳过
download_application() {    
  for i in ${apk_array[@]}; do
    # printf '%s\n' $i;
    value=$(jq -r ".android.value[] | select(has(\"$i\")) | .\"$i\"" cfg.json)
    if [ "$value" == "y" ]; then
      url=$(jq -r ".android.url[] | select(has(\"$i\")) | .\"$i\"" cfg.json)
      if [[ "$url" == \$\(* ]]; then url=$(eval echo "$url"); fi
      # echo "$url"
      apk_files+=($(basename "$url"))
      if [[ ! -f $(basename "$url") ]]; then wget "$url"; fi
      
    fi
  done
}

# 安装文件
install_application() {    
  # printf '%s\n' "${apk_files[@]}"
  for i in  "${apk_files[@]}"; do
    adb -s $(adb devices | grep -w "device" | awk 'NR==1{print $1}') install "$i"
  done
  echo "所有程序已安装完成,如果要配置科学上网软件的订阅链接的话请按字母 p 健,然后等待python脚本自动配置,完成配置或不需要配置可按Ctrl-c结束"
}

function main() {
  set -e
  
  download_application
  install_application $1
}

main $1
cfg.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# coding: utf-8
#
import signal
import sys
import subprocess
import uiautomator2 as u2
import keyboard
import json
import time

def get_device_id():
    # 获取第第一个已连接的Android设备ID
    try:
        result = subprocess.check_output(['adb', 'devices']).decode('utf-8')
        devices = [line.split('\t')[0]
            for line in result.splitlines()
            if '\tdevice' in line]
        return devices[0] if devices else None
    except (subprocess.CalledProcessError, IndexError):
        return None

def signal_handler(signal, frame):
    # print('Caught Ctrl+C / SIGINT signal')
    # 在这里添加你想要做的清理操作
    # 例如停止子进程,关闭文件等
    # ...
    # 退出程序的代码
    sys.exit(0)

# def parse_json_airport():
#     with open('cfg.json', 'r') as fcc_file:
#         fcc_data = json.load(fcc_file)
#         print(fcc_data)
def parse_json_airport():
    with open('cfg.json', 'r') as file:
        data = file.read()
        data_dict = json.loads(data)
        # name_value = data_dict['airport']
        # print(name_value)
        # # 或
        # airport_value = data_dict.get('airport', 'airport not found')
        # print(f"Name: {name_value}, airport: {airport_value}")
        sub_value=data_dict['airport'] ['airport_name']
        # print(test_value)
        return sub_value    
    
def main():
    running = True
    
    device_id = get_device_id()    
    if not device_id:
        print("Error: No connect Android device found")
        sys.exit(1)

    print(f"Connected device: {device_id}")

    # 初始化uiautomator2连接
    try:
        d = u2.connect(device_id)
        print("Device connected successfully")
        
        while running:
            try:
                # 检测元素A是否存在
                if d(resourceId="com.android.packageinstaller:id/vbutton_title", text="继续安装").exists(timeout=0):
                    d(resourceId="com.android.packageinstaller:id/vbutton_title", text="继续安装").click()        
                    print("点击元素")
                    time.sleep(1)  # 操作后等待页面稳定
                    continue  # 回到循环开头重新检测
                elif keyboard.is_pressed('p'): 
                    print("按下了键盘上的 p 键,暂停while循环")

                    # 配置科学上网软件的订阅链接
                    print("开始配置科学上网软件的订阅链接")
                    print("开始调用 parse_json_airport()")
                    airport_sub_value = parse_json_airport() # 获取json文件中配置的订阅链接

                    # 以下两个软件的界面操作是根据软件界面的实际控件进行操作的,如果更换其他软件了,请根据软件的控制进行修改
                    print("配置v2rayNG订阅链接")
                    d.app_stop('com.v2ray.ang')
                    d.app_start('com.v2ray.ang')    
                    d(description="Open navigation drawer").click()    
                    d(resourceId="com.v2ray.ang:id/design_menu_item_text", text="订阅分组设置").click()
                    d(resourceId="com.v2ray.ang:id/add_config").click()
                    d(resourceId="com.v2ray.ang:id/et_remarks").click()
                    d.send_keys("tolink", clear=True)
                    d(resourceId="com.v2ray.ang:id/et_url").click()                    
                    d.send_keys(airport_sub_value, clear=True)
                    d(resourceId="com.v2ray.ang:id/save_config").click()
                    time.sleep(1.5)
                    d.app_stop('com.v2ray.ang')

                    print("配置clash-meta订阅链接")
                    d.app_stop('com.github.metacubex.clash.meta')
                    d.app_start('com.github.metacubex.clash.meta')
                    d(resourceId="com.github.metacubex.clash.meta:id/text_view", text="配置").click()
                    d(resourceId="com.github.metacubex.clash.meta:id/add_view").click()
                    d.xpath('//*[@resource-id="com.github.metacubex.clash.meta:id/main_list"]/android.widget.LinearLayout[2]').click()
                    d(resourceId="com.github.metacubex.clash.meta:id/text_view", text="新配置").click()
                    d.send_keys("tolink", clear=True)    
                    d(resourceId="android:id/button1").click()
                    d(resourceId="com.github.metacubex.clash.meta:id/text_view", text="仅接受 http(s) 和 content 类型").click()                    
                    d.send_keys(airport_sub_value, clear=True)
                    d(resourceId="android:id/button1").click()
                    d(resourceId="com.github.metacubex.clash.meta:id/action_layout").click()
                    d.xpath('//*[@resource-id="com.github.metacubex.clash.meta:id/main_list"]/android.widget.RelativeLayout[1]/android.widget.RadioButton[1]').click()
                    time.sleep(1.5)
                    d.app_stop('com.github.metacubex.clash.meta')
                    
                    input("按回车继续,然后按字母 q 键退出或按 Ctrl-c 结束")
                elif keyboard.is_pressed('q'):                    
                    print("按下了键盘上的 'q' 键,退出while循环")
                    running = False
                else:
                    # print("未找到目标元素,等待重试...")
                    time.sleep(0.5)  # 降低CPU占用
            except Exception as e:
                print(f"发生异常: {e},尝试重新连接设备")                
                d = u2.connect(device_id) # 重新初始化设备连接
                time.sleep(2)
                
        return d
    
    except Exception as e:
        print(f"Connected failed: {str(e)}")
        sys.exit(1)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    d = main()