UIAutomation:Python 自动获取 QQ 群成员资料

news2024/9/27 15:27:07

        出于兴趣,想要获取 “QQ 群”成员资料,于是乎找到了一个自动化的 Python 仓库:Python-UIAutomation-for-Windows。

目录

1. 简介

1.1 实际环境

1.2 安装/源码

1.2.1 pip安装

1.2.2 源码

2. 代码

2.1 全部代码

2.2 class QQGroupMembers

2.2.1 def countdown

2.2.2 更多资料

2.2.3 def guess

2.2.4 def config

2.2.5 保存

3. 演示


1. 简介

        引用仓库作者 Yinkai Sheng 的话,来介绍:

uiautomation 封装了微软 UIAutomation API,支持自动化 Win32MFCWPFModern UI(Metro UI)QtIEFirefoxversion<=56 or >=60Firefox57是第一个 Rust 开发版本,前几个 Rust 开发版本个人测试发现不支持), Chrome 和基于 Electron 开发的应用程序(Chrome 浏览器和 Electron 应用需要加启动参数 --force-renderer-accessibility 才能支持 UIAutomation).

uiautomation is shared under the Apache Licence 2.0.

1.1 实际环境

        个人对操作系统并不是很了解,以下给出我的实际环境。

  • 操作系统Windows 11 教育版
  • 系统类型64 位操作系统, 基于 x64 的处理器
  • Python3.8.19

1.2 安装/源码

        对于 Python 包的使用,可以直接通过 pip 安装,或者使用源码(个人使用源码)。

1.2.1 pip安装

        使用 pip 安装包即可。

pip install uiautomation
1.2.2 源码

        使用源码较为简单,因为代码作者已经将全部类和方法放置于一个 .py 文件:yinkaisheng/Python-UIAutomation-for-Windows/uiautomation/uiautomation.py。

        必要的只有 yinkaisheng/Python-UIAutomation-for-Windows/uiautomation 这一个文件夹即可。

yinkaisheng/Python-UIAutomation-for-Windows/uiautomation
yinkaisheng/Python-UIAutomation-for-Windows/uiautomation

        当然,作者提供了很多使用的 demos,也可以下载下来参考。本文即参考 demos/get_qq_group_members.py 进行的修改与实践。

        注意,uiautomation/bin 中文件作用暂时不清楚,我看源码有对该文件的访问。但是我的测试,在以下代码执行过程中,bin中的文件不是强制被需要的

2. 代码

2.1 全部代码

        先放全部的代码,代码解析在后续。

# coding=utf-8
# @Author: Fulai Cui (cuifulai@mail.hfut.edu.cn)
# @Time: 2024/9/23 11:34
import argparse

import re
from datetime import datetime
import json
import time
from typing import Optional

from tqdm import trange

import uiautomation as auto


class QQGroupMembers:
    def __init__(
            self,
            file_path: Optional[str] = None,
    ):
        self.file_path = file_path

    def main(self):
        auto.Logger.WriteLine('【提示】请把鼠标放在QQ群聊天窗口中右下角群成员列表中的一个成员上面,3秒后获取。',
                              auto.ConsoleColor.Cyan, writeToFile=False)
        self.countdown(3)

        control = auto.ControlFromCursor()
        if control.ControlType != auto.ControlType.ListItemControl:
            auto.Logger.WriteLine('【警告】没有放在群成员上面,程序退出!',
                                  auto.ConsoleColor.Red, writeToFile=False)
            return

        window = auto.GetConsoleWindow()
        if window:
            window.SetActive()

        group = control.GetParentControl()
        members = group.GetChildren()
        for member in members:
            auto.Logger.WriteLine(member.Name,
                                  auto.ConsoleColor.Green, writeToFile=False)
            pass

        auto.Logger.WriteLine('【提示】是否获取成员详细信息?按F9继续,F10退出。',
                              auto.ConsoleColor.Cyan, writeToFile=False)
        self.waite()

        auto.Logger.WriteLine('【提示】3秒后开始获取QQ群成员详细资料,您可以一直按住F10键暂停脚本。',
                              auto.ConsoleColor.Cyan, writeToFile=False)
        self.countdown(3)

        # 确保群里第一个成员可见在最上面
        group.Click()
        group.SendKeys('{Home}', waitTime=0.5)
        for member in members:
            if member.ControlType == auto.ControlType.ListItemControl:
                if auto.IsKeyPressed(auto.Keys.VK_F10):
                    if window:
                        window.SetActive()
                    auto.Logger.WriteLine('【提示】您暂停了脚本,按F9继续。',
                                          auto.ConsoleColor.Cyan, writeToFile=False)
                    while True:
                        if auto.IsKeyPressed(auto.Keys.VK_F9):
                            break
                        time.sleep(0.05)

                member.RightClick(waitTime=0.5)
                menu = auto.MenuControl(searchDepth=1, ClassName='TXGuiFoundation')
                menu_items = menu.GetChildren()
                for menu_item in menu_items:
                    if menu_item.Name == '查看资料':
                        menu_item.Click(40)
                        break
                auto.Logger.WriteLine(json.dumps(self.get_person_detail(), ensure_ascii=False),
                                      auto.ConsoleColor.Green, logFile=self.file_path)
                member.Click()
                auto.SendKeys('{Down}')

    def get_person_detail(self):
        detail_window = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', SubName='的资料')
        for control, _ in auto.WalkControl(detail_window):
            if isinstance(control, auto.ButtonControl):
                if control.Name == '更多资料':
                    control.Click()
                    break

        details = {}
        for control, depth in auto.WalkControl(detail_window):

            key, value = self.guess(control, depth)
            if key is None or key == '':
                continue

            if key not in details:
                details[key] = value
            else:
                details[key] += "-|-" + value
        detail_window.Click(-10, 10)
        return details

    @staticmethod
    def guess(control: auto.Control, depth: int):
        key = None
        value = None
        if isinstance(control, auto.ButtonControl):
            value = control.Name

            if value != '':
                if depth == 8 and re.match(r'^\d+$', value) and control.ControlType == auto.ControlType.ButtonControl:
                    key = '点赞记录'
        elif isinstance(control, auto.TextControl):
            value = control.Name

            if value != '':
                if '天' in value:
                    key = '连续登陆天数'
                    value = value
        elif isinstance(control, auto.PaneControl):
            value = control.Name

            if value != '':
                if '等级' in value:
                    key = '等级'
                    value = re.findall(r'(\d+)', value)[0]
        elif isinstance(control, auto.EditControl):
            key = control.Name
            value = control.GetValuePattern().Value

            if key == '' and value != '':
                if re.match(r'^\d{5,}$', value):
                    key = 'QQ号'
                elif '月' in value:
                    key = '生日'
                else:
                    key = '网名/备注'

        return key, value

    @staticmethod
    def countdown(seconds: int):
        for _ in trange(seconds, desc='倒计时'):
            time.sleep(1)

    @staticmethod
    def waite():
        while True:
            if auto.IsKeyPressed(auto.Keys.VK_F9):
                break
            elif auto.IsKeyPressed(auto.Keys.VK_F10):
                return
            time.sleep(0.05)


def config(args):
    auto.SEARCH_INTERVAL = args.SEARCH_INTERVAL if hasattr(args, 'SEARCH_INTERVAL') else auto.SEARCH_INTERVAL
    auto.MAX_MOVE_SECOND = args.MAX_MOVE_SECOND if hasattr(args, 'MAX_MOVE_SECOND') else auto.MAX_MOVE_SECOND
    auto.TIME_OUT_SECOND = args.TIME_OUT_SECOND if hasattr(args, 'TIME_OUT_SECOND') else auto.TIME_OUT_SECOND
    auto.OPERATION_WAIT_TIME = args.OPERATION_WAIT_TIME if hasattr(args, 'OPERATION_WAIT_TIME') else auto.OPERATION_WAIT_TIME
    auto.MAX_PATH = args.MAX_PATH if hasattr(args, 'MAX_PATH') else auto.MAX_PATH
    auto.DEBUG_SEARCH_TIME = args.DEBUG_SEARCH_TIME if hasattr(args, 'DEBUG_SEARCH_TIME') else auto.DEBUG_SEARCH_TIME
    auto.DEBUG_EXIST_DISAPPEAR = args.DEBUG_EXIST_DISAPPEAR if hasattr(args, 'DEBUG_EXIST_DISAPPEAR') else auto.DEBUG_EXIST_DISAPPEAR
    auto.S_OK = args.S_OK if hasattr(args, 'S_OK') else auto.S_OK


def parse_args():
    parser = argparse.ArgumentParser(description='QQ Group Members')
    parser.add_argument('--dir_path', type=str, default='../logs',
                        help='日志文件路径')
    parser.add_argument('--group_name', type=str, default='XXX群',
                        help='QQ群名称')
    parser.add_argument('--MAX_MOVE_SECOND', type=int, default=0.5,
                        help='最大移动秒数')
    return parser.parse_args()


def main():
    args = parse_args()
    config(args)

    automation = QQGroupMembers(file_path=f"{args.dir_path}/{args.group_name}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log")
    automation.main()


if __name__ == '__main__':
    main()

2.2 class QQGroupMembers

        以下详述一些 demos 中没有的内容。

2.2.1 def countdown

        设置倒计时。

@staticmethod
def countdown(seconds: int):
    for _ in trange(seconds, desc='倒计时'):
        time.sleep(1)
2.2.2 更多资料

        在打开“查看资料”面板之后,先找到“更多资料”,然后点击 .Click() 打开,目的是获取全面的资料信息。

def get_person_detail(self):
    detail_window = auto.WindowControl(searchDepth=1, ClassName='TXGuiFoundation', SubName='的资料')
    for control, _ in auto.WalkControl(detail_window):
        if isinstance(control, auto.ButtonControl):
            if control.Name == '更多资料':
                control.Click()
                break
...more code
2.2.3 def guess

        猜测控件中的内容是什么,这个需要根据实践做出适当的调整

@staticmethod
def guess(control: auto.Control, depth: int):
    key = None
    value = None
    if isinstance(control, auto.ButtonControl):
        value = control.Name

        if value != '':
            if depth == 8 and re.match(r'^\d+$', value) and control.ControlType == auto.ControlType.ButtonControl:
                key = '点赞记录'
    elif isinstance(control, auto.TextControl):
        value = control.Name

        if value != '':
            if '天' in value:
                key = '连续登陆天数'
                value = value
    elif isinstance(control, auto.PaneControl):
        value = control.Name

        if value != '':
            if '等级' in value:
                key = '等级'
                value = re.findall(r'(\d+)', value)[0]
    elif isinstance(control, auto.EditControl):
        key = control.Name
        value = control.GetValuePattern().Value

        if key == '' and value != '':
            if re.match(r'^\d{5,}$', value):
                key = 'QQ号'
            elif '月' in value:
                key = '生日'
            else:
                key = '网名/备注'

    return key, value

        这里,猜测了:

  • ButtonControl.Name,在第 8 层,如果完全是数字,则更可能是“点赞记录
  • TextControl.Name,如果包含“天”,则更可能是“连续登陆天数
  • PaneControl.Name,如果包含“等级”,则更可能是“等级
  • EditControl,有 Name GetValuePattern().Value,如果 Value 完全是数字,则更可能是“QQ号”;如果 Value 包含“月”,则更可能是“生日”;否则的话是“网名/备注”。
2.2.4 def config

        为了修改 uiautomation 的默认配置参数,这里只修改了 uiautomation.MAX_MOVE_SECOND(默认为 1,感觉有点久)。别的参数也可自行修改。

def config(args):
    auto.SEARCH_INTERVAL = args.SEARCH_INTERVAL if hasattr(args, 'SEARCH_INTERVAL') else auto.SEARCH_INTERVAL
    auto.MAX_MOVE_SECOND = args.MAX_MOVE_SECOND if hasattr(args, 'MAX_MOVE_SECOND') else auto.MAX_MOVE_SECOND
    auto.TIME_OUT_SECOND = args.TIME_OUT_SECOND if hasattr(args, 'TIME_OUT_SECOND') else auto.TIME_OUT_SECOND
    auto.OPERATION_WAIT_TIME = args.OPERATION_WAIT_TIME if hasattr(args, 'OPERATION_WAIT_TIME') else auto.OPERATION_WAIT_TIME
    auto.MAX_PATH = args.MAX_PATH if hasattr(args, 'MAX_PATH') else auto.MAX_PATH
    auto.DEBUG_SEARCH_TIME = args.DEBUG_SEARCH_TIME if hasattr(args, 'DEBUG_SEARCH_TIME') else auto.DEBUG_SEARCH_TIME
    auto.DEBUG_EXIST_DISAPPEAR = args.DEBUG_EXIST_DISAPPEAR if hasattr(args, 'DEBUG_EXIST_DISAPPEAR') else auto.DEBUG_EXIST_DISAPPEAR
    auto.S_OK = args.S_OK if hasattr(args, 'S_OK') else auto.S_OK
2.2.5 保存

        使用 uiautomation.Logger.WriteLine() 方法来写入内容。注意,需要在定义 QQGroupMembers 对象时,设置参数 file_path

3. 演示

        此演示无别的意图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

16. C++ TinyWebServer项目总结(16. 服务器调制、调试和测试)

主要包括&#xff1a; 使用 tcpdump 抓包&#xff1b;使用 gdb 调试器&#xff1b;使用压力测试工具&#xff0c;模拟现实世界中的高并发请求&#xff0c;测试服务器在高压状态下的稳定性。 最大文件描述符数 Linux 对应用进程能打开的最大文件描述符数量有两个层次的限制&a…

升压站成套设备厂家

那么&#xff0c;本文呢&#xff0c;就是将围绕着升压站设备厂家这个关键词&#xff0c;来为您、为潜在的光伏升压站成套设备的采购们分享 一些干货&#xff0c;以及说说升压站设备生成厂家的情况。 我知道&#xff0c;很多人关注的所谓的升压站设备或许比较的多。比如包括了逆…

Flutter路由

路由作为一种页面切换的能力&#xff0c;非常重要。Flutter 中路由管理有几个重要的点。 Navigator 1.0&#xff1a;Flutter 早期路由系统&#xff0c;侧重于移动端 &#xff0c;命令式编程风格&#xff0c;使用 Navigator.push() 和 Navigator.pop() 等方法来管理路由栈。 N…

序列化流(对象操作输出流)反序列化流(对象操作输入流)

可以把Java中的对象写到本地文件中 序列化流&#xff08;对象操作输出流&#xff09; 构造方法 成员方法 使用对象输出流将对象保存到文件会出现NotSerializableException异常 解决方案&#xff1a;需要让Javabean类实现Serializable接口 Student package myio;import java.…

Claude 的上下文检索功能提升了 RAG 准确率,这会是人工智能革命?

前言 在人工智能领域不断进步的过程中&#xff0c;人们对更准确且具备上下文理解能力的响应的追求&#xff0c;催生了诸多突破性创新。 而 Claude 的上下文检索技术就是其中一项进步&#xff0c;有望显著提升检索增强生成 (RAG) 系统的表现。 可能有同学就要问了&#xff1a;…

DDL 超时,应该如何解决 | OceanBase 用户问题集萃

问题背景 在OceanBase的社区问答里常看到有用户发帖提出DDL超时的问题&#xff0c; 如“执行 DDL 超时&#xff0c;为何调大超时时间不生效&#xff1f;” 。但很多帖子的回答都没有完美解决。因此&#xff0c;这里把相关的解决思路在这里分享给大家。 帖子里对这类问题的描述…

2、 如何提高电脑运行速度 (改虚拟内存)?

改下电脑C磁盘的虚拟内存 方法如下&#xff1a; ① 按下电脑键盘上的 win E 键 &#xff0c; 然后鼠标移动到左边的【此电脑上】 然后&#xff0c;按下鼠标右键&#xff0c;选择【属性】 ② 然后&#xff0c;选择【高级系统设置】 4、选择【高级】&#xff0c;选择性能里面…

SPSS26统计分析笔记——5 卡法检验

1 卡方检验原理 卡方检验由卡尔皮尔逊&#xff08;Karl Pearson&#xff09;于1900年首次提出&#xff0c;是一种针对频数数据&#xff08;定类数据或计数数据&#xff09;的假设检验方法。它通过比较实际观测次数与理论期望次数之间的差异&#xff0c;构造出 χ 2 {\chi^2} χ…

seL4 Threads(四)

官网链接: Threads Threads 这篇教程主要是使用seL4中的threads。 TCB Thread Control Blocks seL4提供了线程代表执行的上下文以及管理处理器时间。seL4中的线程是通过线程控制块对象&#xff08;TCB&#xff09;实现的&#xff0c;每个内核线程都有一个线程控制块。 线程…

linux服务器安装原生的php环境

在CentOS上安装原生的PHP环境相对简单。下面是一个详细的步骤指南&#xff0c;适用于CentOS 7及更高版本。 ### 第一步&#xff1a;更新系统 首先&#xff0c;确保你的系统是最新的&#xff1a; sudo yum update -y ### 第二步&#xff1a;安装EPEL和Remi仓库 1. **安装EP…

Windows内核编程基础(3)

内存分配 在应用层编程时&#xff0c;系统提供了GlobalAlloc/HeapAlloc/LocalAlloc等函数。C/C库提供了malloc函数&#xff0c;以及new操作符在堆上分配内存。 在我前面一个关于Windows页交换文件的博客中&#xff0c;介绍了虚拟内存&#xff0c; 虚拟内存是计算机系统内存管…

古月居全新改版上线:AI 大模型“古月知道”引领 ROS 学习新体验

前言 古月居自成立以来&#xff0c;一直致力于为广大 ROS&#xff08;机器人操作系统&#xff09;爱好者和开发者提供优质的学习资源和社区交流平台。经过长期的用户调研和反馈&#xff0c;我们发现旧版古月居在使用过程中存在一些不便之处。 为了更好地服务大家&#xff0c;…

如何生成谷歌临时邮箱?

谷歌的Gmail作为全球最受欢迎的邮件服务之一&#xff0c;不仅因其稳定性和强大的功能而备受青睐&#xff0c;还因为它支持临时邮箱功能&#xff0c;这一功能能够极大地提升用户在各种场景下的使用灵活性。无论是处理一次性事务、注册新账户还是防止垃圾邮件&#xff0c;Gmail的…

通义模型Prompt调优的实用技巧

1. 目录 1. prompt工程简介 2. Prompt设计 2.1 Prompt主要构成要素 2.2 Prompt编写策略 策略一&#xff1a;对较难被准确遵循的复杂规则可拆分为多条规则&#xff0c;有助于提升效果 策略二&#xff1a;适当冗余关键信息 策略三&#xff1a;使用分隔符给Prompt分段 策…

类与对象—python

一、类的含义 1.1类的作用&#xff08;理解&#xff09; 收集学生信息时&#xff0c;如果让同学们自主填写&#xff0c;信息的顺序、格式不一&#xff0c;内容混乱。如果发给同学们既定的表格&#xff0c;同学们按照规定的顺序、格式进行填写&#xff0c;那信息就会一目了然&…

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SO-SVR蛇群算法优化支持向量机的数据多…

path_provider插件的用法

文章目录 1. 概念介绍2. 实现方法3. 示例代码我们在上一章回中介绍了"如何实现本地存储"相关的内容,本章回中将介绍如何实现文件存储.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在上一章回中介绍的本地存储只能存储dart语言中基本类型的数值,如果遇到…

大数据-147 Apache Kudu 常用 Java API 增删改查

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【学习笔记】 AD24中元器件重叠系统不报错的解决方案(消除报错)

【学习笔记】 AD24中PCB设计元器件重叠后系统不报错的解决方案&#xff08;如何主动屏蔽报错&#xff09; 一、Component Clearance未开启使能的解决方案二、最小水平间距设置错误的解决方案三、未开启设计规则检查的解决方案四、设计规则检查中 “在线”和“批量”的含义五、为…

开源的CDN:jsDelivr+Github加速图片加载

文章目录 20240530更新 网站加载的图片耗时&#xff0c;将图片使用jsDelivr进行加速。 每次打开静态网站的时候&#xff0c;都会发现页面的内容已经加载出来了&#xff0c;但是图片还是一片白&#xff0c;就考虑如何让图片能够更快的加载出来。 后面发现可以用jsDelivr加速Gi…