【Python】通过第三方库wxauto自动化操作微信电脑客户端
admin 阅读: 2024-03-21
后台-插件-广告管理-内容页头部广告(手机) |
文章目录
- 一.简介
- 二.wxauto提供的函数
- 1.WxUtils类功能函数:
- 2.WeChat类主要函数:
- 三.使用
- 四.遇到的问题
一.简介
wxauto是一个Python第三方库,用于自动化操作微信电脑客户端通过wxauto,我们可以使用Python编写脚本,实现以下功能
- 获取微信好友列表、群组列表、聊天记录等信息。
- 在微信中发送文本、图片、语音等信息给好友或群组。
- 自动回复好友或群组的消息。
- 自动加入或退出群组。
- 自动发送文件给好友或群组。
- 自动发送红包给好友或群组。
- 其他自定义的自动化操作。
使用wxauto需要先安装其库文件,可以使用pip命令进行安装
pip install wxauto- 1
二.wxauto提供的函数
wxauto目前有WxParam、WxUtils、WeChat三个类:
- 其中WxParam设置基本参数设置。
1.WxUtils类功能函数:
- SetClipboard(data, dtype=‘text’) 复制文本信息或图片到剪贴板data : 要复制的内容,str 或 Image 图像;
- Screenshot(hwnd, to_clipboard=True) 为句柄为hwnd的窗口程序截图;hwnd : 句柄;to_clipboard : 是否复制到剪贴板;
- SavePic(savepath=None, filename=None) 保存截图;savepath:文件保存位置;filename:文件名字;
- ControlSize(control) 获取控制窗口大小;
- ClipboardFormats(unit=0, *units) 获取剪切板格式 ;
- CopyDict()
2.WeChat类主要函数:
- GetSessionList(self, reset=False) 获取当前会话列表,更新会话列表
- Search(self, keyword) 查找微信好友或关键词;keywords: 要查找的关键词,最好完整匹配,不完全匹配只会选取搜索框第一个;
- ChatWith(self, who, RollTimes=None) 打开某个聊天框;who : 要打开聊天框的好友名,最好完整匹配,不完全匹配只会选取搜索框第一个;RollTimes : 默认向下滚动次数,再进行搜索;
- SendMsg(self, msg, clear=True) 向当前窗口发送消息;msg : 要发送的消息;
- SendFiles(self, *filepath, not_exists=‘ignore’) 向当前聊天窗口发送文件;not_exists: 如果未找到指定文件,继续或终止程序;*filepath: 要复制文件的绝对路径;
- SendClipboard(self) 向当前聊天页面发送剪贴板复制的内容;
- GetAllMessage(self) 获取当前窗口中加载的所有聊天记录;
- GetLastMessage(self) 获取当前窗口中最后一条聊天记录
- LoadMoreMessage(self, n=0.1) 定位到当前聊天页面,并往上滚动鼠标滚轮,加载更多聊天记录到内存发送某个桌面程序的截图,如:微信、记事本;name : 要发送的桌面程序名字;classname : 要发送的桌面程序类别名;
- SendScreenshot(self, name=None, classname=None) 发送某个桌面程序的截图,如:微信、记事本;name : 要发送的桌面程序名字;classname : 要发送的桌面程序类别名;
三.使用
from wxauto import * # 获取当前微信客户端 wx = WeChat() # 获取会话列表 wx.GetSessionList() # 输出当前聊天窗口聊天消息 msgs = wx.GetAllMessage for msg in msgs: print('%s : %s'%(msg[0], msg[1])) ## 获取更多聊天记录 wx.LoadMoreMessage() msgs = wx.GetAllMessage for msg in msgs: print('%s : %s'%(msg[0], msg[1])) # 向某人发送消息(以`文件传输助手`为例) msg = '你好~' who = '文件传输助手' wx.ChatWith(who) # 打开`文件传输助手`聊天窗口 wx.SendMsg(msg) # 向`文件传输助手`发送消息:你好~ ## 发送换行消息(最近很多人问换行消息如何发送,新增说明一下) msg = '''你好 这是第二行 这是第三行 这是第四行''' who = '文件传输助手' WxUtils.SetClipboard(msg) # 将内容复制到剪贴板,类似于Ctrl + C wx.ChatWith(who) # 打开`文件传输助手`聊天窗口 wx.SendClipboard() # 发送剪贴板的内容,类似于Ctrl + V # 向某人发送文件(以`文件传输助手`为例,发送三个不同类型文件) file1 = 'D:/test/wxauto.py' file2 = 'D:/test/pic.png' file3 = 'D:/test/files.rar' who = '文件传输助手' wx.ChatWith(who) # 打开`文件传输助手`聊天窗口 wx.SendFiles(file1, file2, file3) # 向`文件传输助手`发送上述三个文件 # 注:为保证发送文件稳定性,首次发送文件可能花费时间较长,后续调用会缩短发送时间 # 向某人发送程序截图(以`文件传输助手`为例,发送微信截图) name = '微信' classname = 'WeChatMainWndForPC' wx.ChatWith(who) # 打开`文件传输助手`聊天窗口 wx.SendScreenshot(name, classname) # 发送微信窗口的截图给文件传输助手 注:为保证发送文件稳定性,首次发送文件可能花费时间较长,后续调用会缩短发送时间- 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
四.遇到的问题
由于部分版本的微信可能由于UI界面不同从而无法使用,截至2022-06-10最新版本可用
-
因此在代码运行的时候,会发现图片无法发送、消息无法发送的报错情况 LookupError: Find Control Timeout(10s): {Name: '输入', ControlType: EditControl},这是因为pip 下载的wxauto第三方库无法匹配微信客户端3.7的版本。
-
如果有遇到,可以通过Github的方式下载源码,直接修改源码
wxauto库信息: Author: tikic@qq.com Source: https://github.com/cluic/wxauto Version: 3.3.5.3- 1
- 2
- 3
- 4
微信最新版本需要重新适配,需修改 wxauto.py 代码
1.在 wxauto.py 的文件中找到 WeChat 的类,并添加下述方法 def ChangeWindow(self, window_title): self.EditMsg = self.UiaAPI.EditControl(Name=f'{window_title}') 2.之后在 ChatWith 方法中加入如下代码 def ChatWith(self, who, RollTimes=None): ''' 打开某个聊天框 who : 要打开的聊天框好友名,str; * 最好完整匹配,不完全匹配只会选取搜索框第一个 RollTimes : 默认向下滚动多少次,再进行搜索 ''' self.UiaAPI.SwitchToThisWindow() self.ChangeWindow(who) # [2] 加入如下方法,在每次更改聊天对象时调用 ChangeWindow 方法 ... ...- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
wxauto.py完整代码
#!python3 # -*- coding: utf-8 -*- """ Author: tikic@qq.com Source: https://github.com/cluic/wxauto License: MIT License Version: 3.9.0.28 """ import uiautomation as uia import win32gui, win32con import win32clipboard as wc import time import os AUTHOR_EMAIL = 'tikic@qq.com' UPDATE = '2023-02-25' VERSION = '3.9.0.28' class WxParam: SYS_TEXT_HEIGHT = 33 TIME_TEXT_HEIGHT = 34 RECALL_TEXT_HEIGHT = 45 CHAT_TEXT_HEIGHT = 52 CHAT_IMG_HEIGHT = 117 SpecialTypes = ['[文件]', '[图片]', '[视频]', '[音乐]', '[链接]'] class WxUtils: def GetMessageInfos(Item, msglist=None): msglist = msglist if msglist is not None else list() if len(Item.GetChildren()) == 0: msglist.append(Item.Name) else: for i in Item.GetChildren(): WxUtils.GetMessageInfos(i, msglist) return [i for i in msglist if i] def SplitMessage(MsgItem): uia.SetGlobalSearchTimeout(0) MessageInfos = WxUtils.GetMessageInfos(MsgItem) MsgItemName = MsgItem.Name if MsgItem.BoundingRectangle.height() == WxParam.SYS_TEXT_HEIGHT: Msg = ('SYS', MsgItemName, MessageInfos) elif MsgItem.BoundingRectangle.height() == WxParam.TIME_TEXT_HEIGHT: Msg = ('Time', MsgItemName, MessageInfos) elif MsgItem.BoundingRectangle.height() == WxParam.RECALL_TEXT_HEIGHT: if '撤回' in MsgItemName: Msg = ('Recall', MsgItemName, MessageInfos) else: Msg = ('SYS', MsgItemName, MessageInfos) else: Index = 1 User = MsgItem.ButtonControl(foundIndex=Index) try: while True: if User.Name == '': Index += 1 User = MsgItem.ButtonControl(foundIndex=Index) else: break Msg = (User.Name, MsgItemName, MessageInfos) except: Msg = ('SYS', MsgItemName, MessageInfos) uia.SetGlobalSearchTimeout(10.0) return Msg def SetClipboard(data, dtype='text'): '''复制文本信息或图片到剪贴板 data : 要复制的内容,str 或 Image 图像''' if dtype.upper() == 'TEXT': type_data = win32con.CF_UNICODETEXT elif dtype.upper() == 'IMAGE': from io import BytesIO type_data = win32con.CF_DIB output = BytesIO() data.save(output, 'BMP') data = output.getvalue()[14:] else: raise ValueError('param (dtype) only "text" or "image" supported') wc.OpenClipboard() wc.EmptyClipboard() wc.SetClipboardData(type_data, data) wc.CloseClipboard() def Screenshot(hwnd, to_clipboard=True): '''为句柄为hwnd的窗口程序截图 hwnd : 句柄 to_clipboard : 是否复制到剪贴板 ''' import pyscreenshot as shot bbox = win32gui.GetWindowRect(hwnd) win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, \ win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, \ win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) win32gui.BringWindowToTop(hwnd) im = shot.grab(bbox) if to_clipboard: WxUtils.SetClipboard(im, 'image') return im def SavePic(savepath=None, filename=None): Pic = uia.WindowControl(ClassName='ImagePreviewWnd', Name='图片查看') Pic.SendKeys('{Ctrl}s') SaveAs = Pic.WindowControl(ClassName='#32770', Name='另存为...') SaveAsEdit = SaveAs.EditControl(ClassName='Edit', Name='文件名:') SaveButton = Pic.ButtonControl(ClassName='Button', Name='保存(S)') PicName, Ex = os.path.splitext(SaveAsEdit.GetValuePattern().Value) if not savepath: savepath = os.getcwd() if not filename: filename = PicName FilePath = os.path.realpath(os.path.join(savepath, filename + Ex)) SaveAsEdit.SendKeys(FilePath) SaveButton.Click() Pic.SendKeys('{Esc}') def ControlSize(control): locate = control.BoundingRectangle size = (locate.width(), locate.height()) return size def ClipboardFormats(unit=0, *units): units = list(units) wc.OpenClipboard() u = wc.EnumClipboardFormats(unit) wc.CloseClipboard() units.append(u) if u: units = WxUtils.ClipboardFormats(u, *units) return units def CopyDict(): Dict = {} for i in WxUtils.ClipboardFormats(): if i == 0: continue wc.OpenClipboard() try: content = wc.GetClipboardData(i) wc.CloseClipboard() except: wc.CloseClipboard() raise ValueError if len(str(i)) >= 4: Dict[str(i)] = content return Dict class WeChat: def __init__(self): self.UiaAPI = uia.WindowControl(ClassName='WeChatMainWndForPC') self.SessionList = self.UiaAPI.ListControl(Name='会话') self.EditMsg = self.UiaAPI.EditControl(Name='输入') self.SearchBox = self.UiaAPI.EditControl(Name='搜索') self.MsgList = self.UiaAPI.ListControl(Name='消息') self.SessionItemList = [] def ChangeWindow(self, window_title): self.EditMsg = self.UiaAPI.EditControl(Name=f'{window_title}') def GetSessionList(self, reset=False): '''获取当前会话列表,更新会话列表''' self.SessionItem = self.SessionList.ListItemControl() SessionList = [] if reset: self.SessionItemList = [] for i in range(100): try: name = self.SessionItem.Name except: break if name not in self.SessionItemList: self.SessionItemList.append(name) if name not in SessionList: SessionList.append(name) self.SessionItem = self.SessionItem.GetNextSiblingControl() return SessionList def Search(self, keyword): ''' 查找微信好友或关键词 keywords: 要查找的关键词,str * 最好完整匹配,不完全匹配只会选取搜索框第一个 ''' self.UiaAPI.SetFocus() time.sleep(0.2) self.UiaAPI.SendKeys('{Ctrl}f', waitTime=1) self.SearchBox.SendKeys(keyword, waitTime=1.5) self.SearchBox.SendKeys('{Enter}') def ChatWith(self, who, RollTimes=None): ''' 打开某个聊天框 who : 要打开的聊天框好友名,str; * 最好完整匹配,不完全匹配只会选取搜索框第一个 RollTimes : 默认向下滚动多少次,再进行搜索 ''' self.UiaAPI.SwitchToThisWindow() self.ChangeWindow(who) # [2] 加入如下方法,在每次更改聊天对象时调用 ChangeWindow 方法 RollTimes = 10 if not RollTimes else RollTimes def roll_to(who=who, RollTimes=RollTimes): for i in range(RollTimes): if who not in self.GetSessionList()[:-1]: self.SessionList.WheelDown(wheelTimes=3, waitTime=0.1 * i) else: time.sleep(0.5) self.SessionList.ListItemControl(Name=who).Click(simulateMove=False) return 1 return 0 rollresult = roll_to() if rollresult: return 1 else: self.Search(who) return roll_to(RollTimes=1) def SendMsg(self, msg, clear=True): '''向当前窗口发送消息 msg : 要发送的消息 clear : 是否清除当前已编辑内容 ''' self.UiaAPI.SwitchToThisWindow() if clear: self.EditMsg.SendKeys('{Ctrl}a', waitTime=0) self.EditMsg.SendKeys(msg, waitTime=0) self.EditMsg.SendKeys('{Enter}', waitTime=0) def SendFiles(self, *filepath, not_exists='ignore'): """向当前聊天窗口发送文件 not_exists: 如果未找到指定文件,继续或终止程序 filepath (list): 要复制文件的绝对路径""" key = '' for file in filepath: file = os.path.realpath(file) if not os.path.exists(file): if not_exists.upper() == 'IGNORE': print('File not exists:', file) continue elif not_exists.upper() == 'RAISE': raise FileExistsError('File Not Exists: %s' % file) else: raise ValueError('param not_exists only "ignore" or "raise" supported') key += '- 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
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
wxautoapi
声明
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |