利用 PC 端自动化的技术,我半个小时写了个 QQ 聊天群发器!
前言:
近期录制了一个pywinauto版的PC端自动化的小视频,部分同学看了之后,问可以不可以用这个技术来写一个qq聊天群发器?答案当然是可以的。掌握了自动化的技能,写个聊天群发器还不是轻轻松松,于是花了半个小时,给大家写一个初级的QQ聊天群发器,算上代码注释也就100来行代码!话不多说,先上运行的效果图:
思路分析
要实现一个qq聊天群发器,那么我们需要先分析一下思路和qq聊天的群发流程。我们要对一个群里面所有成员进行消息群发,那么首先应该打开该QQ群,然后获取到所有的群成员,然后逐个发送消息,分析一下发现流程非常简单,那么接下来我们通过代码来实现。具体思路如下:
-
1、打开QQ群窗口
-
2、获取所有的群成员
-
3、点击一个群成员,打开窗口
-
4、编辑消息,点击发送
-
5、回到群窗口
-
6、重复第3,4,5步,直到所有的消息发送完毕。
代码实现
1、选中QQ聊天窗口
要进行群发消息首先需要登录qq,打开群聊窗口,由于时间关系,启动qq登录这个步骤哦我就直接跳过了,在这边直接使用pywinauto连接已经启动的qq,选择群聊窗口。代码如下
import pywinauto
# 通过qq的进程号,连接qq这个程序
app = pywinauto.Application(backend="uia").connect(process=13436)
# 选中聊天窗口
dlg = app["Dialog"]
2、查看窗口的控件结构
选中聊天窗口之后,我们要获取群成员,发送消息就需要定位到项目内容的控件才可以进行操作,这边直接打印了聊天窗口所有的控件结构,代码如下:
# 打印窗口控件结构
dlg.print_control_identifiers()
控件结构(部分内容):
上面截图了控件的部分结构,可以看得出来控件层级关系比较多,而且很多层级都没有唯一定位的标识,于是又使用了控件检测工具UIspy检查了一下群成员的控件结构,截图如下:
看着这种毫无标识的控件层级关系,又没有开发小哥哥的help,瞬间有点绝望,控件没有标识,定位起来就会非常困难,(这也是pc端自动化实现的难点之一),不过没有关系,可以简单粗暴的层级关系一级一级往下找。
比如获取全部群成员,写了个简单粗暴的定位代码如下:
members = dlg.children()[2].children()[0].children()[0].children()[0].children()[1].children()[0].children()[0].children()[0].children()[0].children()[0].children()[0].children()[0].children()[2].children()[1]
# 获取所有的群成员
mems = members.items()[0].children()
一眼看上去,有没有想打人的冲动?这是什么定位操作,表示完全看不懂,看不懂就对了。pywinauto中没有跨层级查找子孙控件的方式,只有这样一级一级往下找了,丑是丑了点,但是没关系,先将就着用(毕竟只有半个小时间),后续有时间了,再自己封装一个专门查找子孙控件的方法。
注:关于后面的一些控件定位也先全部采用这种方式来实现。
经过短暂的思考之后,于是封装了以下几个方法:
-
定义一个群发类及初始化方法
import pywinauto from pywinauto.keyboard import send_keys from pywinauto import mouse class MassOfNews: """ qq聊天自动群发器初始版 使用说明:群发前需要打开该群聊窗口,并且把该群聊置顶,然后再运行该程序 """ def __init__(self, process, group_name, info): """ :param process:QQ的进程号 :param gruop_name: 要进行群发的qq群名 :param info: 发送的消息内容 """ self.app = pywinauto.Application(backend="uia").connect(process=process) # 选中聊天窗口 self.dlg = self.app["Dialog"] self.group_name = group_name self.info = info
-
获取群成员
def get_members(self): """ 获取群成员 :return: list类型,包含所有的群成员对象 """ # 点击该qq群 self.get_chat(self.group_name).click_input() # 获取所有群成员 members = self.dlg.children()[2].children() [0].children()[0].children()[0].children()[1].children()[0].children()[0].children()[ 0].children()[0].children()[0].children()[0].children()[0].children()[2].children()[1] mems = members.items()[0].children() return mems
-
点击群成员(聊天显示成员和显示的像素有误差)
def click_member(self, control, index): """qq群成员双击击""" # 获取坐标 rect = control.rectangle().mid_point() # 坐标修正(经过分析发现显示每个成员直接的坐标误差为7像素) rect.y = rect.y + 7 * index # 双击 mouse.double_click(coords=(rect.x, rect.y))
-
获取指定聊天窗口
def get_chat(self, chat_name): """ 获取指定的聊天窗口 :param chat_name: :return: """ chats = self.dlg.children()[2].children()[1].children()[0].children()[0].children()[0].children() for chat in chats: if chat_name in chat.texts(): return chat
-
发送消息的方法
def send_info(self, info): # 获取个人聊天输入窗口控件 try: input_edit = self.dlg.children()[2].children()[0].children()[0].children()[0].children()[1].children()[0].children()[1].children()[0].children()[1].children()[0].children()[0].children()[0] except IndexError: input_edit = self.dlg.children()[2].children()[0].children()[0].children()[0].children()[1].children()[0].children()[0].children()[0].children()[1].children()[0].children()[0].children()[0] # 点击激活输入框 input_edit.click_input() # 消息输入框,输入内容 input_edit.type_keys(self.info) # 回车发送 send_keys("^{VK_RETURN}")
-
启动函数
def run(self): """启动文件""" # 第一步:获取所有的群成员控件 members = self.get_members() # 第二步:遍历群成员,以此点击 for index, item in enumerate(members[:-1]): if item.texts(): print(f"正在给{item.texts()[0]}发送消息:{self.info}") import time time.sleep(2) # 第三步:点击该成员 self.click_member(item, index) # # # 第四步:发送消息(调用发送消息的方法) self.send_info(self.info) # # 第五步:点击回到群窗口 self.get_chat(self.group_name).click_input() else: print("所有成员发送完毕,一共发送了{}人".format(len(members)-1))
-
启动群发程序
if __name__ == '__main__': # 说明:第一个参数是qq的进程,第二个参数为群发的群名,第三个参数为发送的消息 mn = MassOfNews(13436, "python小组", "打扰了,群发消息测试,请忽略!") mn.run()
关于群发的基础功能就这样实现了,当然上述代码还有很多优化的空间,也有一些不完善的地方,后续有时间再给大家更新和优化!大家如果对PC端自动化的技能感兴趣的话,可以私信,获取相关的学习资料哈!
欢迎来到testingpai.com!
注册 关于