一.簡介
wxauto是一個Python第三方庫,用于自動化操作微信電腦客戶端
通過wxauto,我們可以使用Python編寫腳本,實現(xiàn)以下功能
- 獲取微信好友列表、群組列表、聊天記錄等信息。
- 在微信中發(fā)送文本、圖片、語音等信息給好友或群組。
- 自動回復(fù)好友或群組的消息。
- 自動加入或退出群組。
- 自動發(fā)送文件給好友或群組。
- 自動發(fā)送紅包給好友或群組。
- 其他自定義的自動化操作。
使用wxauto需要先安裝其庫文件,可以使用pip命令進行安裝
pip install wxauto
二.wxauto提供的函數(shù)
wxauto目前有WxParam、WxUtils、WeChat三個類:
- 其中WxParam設(shè)置基本參數(shù)設(shè)置。
1.WxUtils類功能函數(shù):
- SetClipboard(data, dtype=‘text’) 復(fù)制文本信息或圖片到剪貼板data : 要復(fù)制的內(nèi)容,str 或 Image 圖像;
- Screenshot(hwnd, to_clipboard=True) 為句柄為hwnd的窗口程序截圖;hwnd : 句柄;to_clipboard : 是否復(fù)制到剪貼板;
- SavePic(savepath=None, filename=None) 保存截圖;savepath:文件保存位置;filename:文件名字;
- ControlSize(control) 獲取控制窗口大?。?/li>
- ClipboardFormats(unit=0, *units) 獲取剪切板格式 ;
- CopyDict()
2.WeChat類主要函數(shù):
- GetSessionList(self, reset=False) 獲取當(dāng)前會話列表,更新會話列表
- Search(self, keyword) 查找微信好友或關(guān)鍵詞;keywords: 要查找的關(guān)鍵詞,最好完整匹配,不完全匹配只會選取搜索框第一個;
- ChatWith(self, who, RollTimes=None) 打開某個聊天框;who : 要打開聊天框的好友名,最好完整匹配,不完全匹配只會選取搜索框第一個;RollTimes : 默認向下滾動次數(shù),再進行搜索;
- SendMsg(self, msg, clear=True) 向當(dāng)前窗口發(fā)送消息;msg : 要發(fā)送的消息;
- SendFiles(self, *filepath, not_exists=‘ignore’) 向當(dāng)前聊天窗口發(fā)送文件;not_exists: 如果未找到指定文件,繼續(xù)或終止程序;*filepath: 要復(fù)制文件的絕對路徑;
- SendClipboard(self) 向當(dāng)前聊天頁面發(fā)送剪貼板復(fù)制的內(nèi)容;
- GetAllMessage(self) 獲取當(dāng)前窗口中加載的所有聊天記錄;
- GetLastMessage(self) 獲取當(dāng)前窗口中最后一條聊天記錄
- LoadMoreMessage(self, n=0.1) 定位到當(dāng)前聊天頁面,并往上滾動鼠標滾輪,加載更多聊天記錄到內(nèi)存發(fā)送某個桌面程序的截圖,如:微信、記事本;name : 要發(fā)送的桌面程序名字;classname : 要發(fā)送的桌面程序類別名;
- SendScreenshot(self, name=None, classname=None) 發(fā)送某個桌面程序的截圖,如:微信、記事本;name : 要發(fā)送的桌面程序名字;classname : 要發(fā)送的桌面程序類別名;
三.使用
from wxauto import *
# 獲取當(dāng)前微信客戶端
wx = WeChat()
# 獲取會話列表
wx.GetSessionList()
# 輸出當(dāng)前聊天窗口聊天消息
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]))
# 向某人發(fā)送消息(以`文件傳輸助手`為例)
msg = '你好~'
who = '文件傳輸助手'
wx.ChatWith(who) # 打開`文件傳輸助手`聊天窗口
wx.SendMsg(msg) # 向`文件傳輸助手`發(fā)送消息:你好~
## 發(fā)送換行消息(最近很多人問換行消息如何發(fā)送,新增說明一下)
msg = '''你好
這是第二行
這是第三行
這是第四行'''
who = '文件傳輸助手'
WxUtils.SetClipboard(msg) # 將內(nèi)容復(fù)制到剪貼板,類似于Ctrl + C
wx.ChatWith(who) # 打開`文件傳輸助手`聊天窗口
wx.SendClipboard() # 發(fā)送剪貼板的內(nèi)容,類似于Ctrl + V
# 向某人發(fā)送文件(以`文件傳輸助手`為例,發(fā)送三個不同類型文件)
file1 = 'D:/test/wxauto.py'
file2 = 'D:/test/pic.png'
file3 = 'D:/test/files.rar'
who = '文件傳輸助手'
wx.ChatWith(who) # 打開`文件傳輸助手`聊天窗口
wx.SendFiles(file1, file2, file3) # 向`文件傳輸助手`發(fā)送上述三個文件
# 注:為保證發(fā)送文件穩(wěn)定性,首次發(fā)送文件可能花費時間較長,后續(xù)調(diào)用會縮短發(fā)送時間
# 向某人發(fā)送程序截圖(以`文件傳輸助手`為例,發(fā)送微信截圖)
name = '微信'
classname = 'WeChatMainWndForPC'
wx.ChatWith(who) # 打開`文件傳輸助手`聊天窗口
wx.SendScreenshot(name, classname) # 發(fā)送微信窗口的截圖給文件傳輸助手
注:為保證發(fā)送文件穩(wěn)定性,首次發(fā)送文件可能花費時間較長,后續(xù)調(diào)用會縮短發(fā)送時間
四.遇到的問題
由于部分版本的微信可能由于UI界面不同從而無法使用,截至2022-06-10
最新版本可用
-
因此在代碼運行的時候,會發(fā)現(xiàn)
圖片無法發(fā)送、消息無法發(fā)送
的報錯情況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
微信最新版本需要重新適配,需修改 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] 加入如下方法,在每次更改聊天對象時調(diào)用 ChangeWindow 方法
... ...
wxauto.py完整代碼文章來源:http://www.zghlxwxcb.cn/news/detail-650070.html
#!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'):
'''復(fù)制文本信息或圖片到剪貼板
data : 要復(fù)制的內(nèi)容,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 : 是否復(fù)制到剪貼板
'''
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):
'''獲取當(dāng)前會話列表,更新會話列表'''
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):
'''
查找微信好友或關(guān)鍵詞
keywords: 要查找的關(guān)鍵詞,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] 加入如下方法,在每次更改聊天對象時調(diào)用 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):
'''向當(dāng)前窗口發(fā)送消息
msg : 要發(fā)送的消息
clear : 是否清除當(dāng)前已編輯內(nèi)容
'''
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'):
"""向當(dāng)前聊天窗口發(fā)送文件
not_exists: 如果未找到指定文件,繼續(xù)或終止程序
filepath (list): 要復(fù)制文件的絕對路徑"""
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 += '<EditElement type="3" filepath="%s" shortcut="" />' % file
self.EditMsg.SendKeys(' ', waitTime=0)
self.EditMsg.SendKeys('{Ctrl}a', waitTime=0)
self.EditMsg.SendKeys('{Ctrl}c', waitTime=0)
self.EditMsg.SendKeys('{Delete}', waitTime=0)
while True:
try:
data = WxUtils.CopyDict()
break
except:
pass
for i in data:
data[i] = data[i].replace(b'<EditElement type="0"><![CDATA[ ]]></EditElement>', key.encode())
data1 = {
'13': '',
'16': b'\x04\x08\x00\x00',
'1': b'',
'7': b''
}
data.update(data1)
wc.OpenClipboard()
wc.EmptyClipboard()
for k, v in data.items():
wc.SetClipboardData(int(k), v)
wc.CloseClipboard()
self.SendClipboard()
return 1
def SendClipboard(self):
'''向當(dāng)前聊天頁面發(fā)送剪貼板復(fù)制的內(nèi)容'''
self.SendMsg('{Ctrl}v')
@property
def GetAllMessage(self):
'''獲取當(dāng)前窗口中加載的所有聊天記錄'''
MsgDocker = []
MsgItems = self.MsgList.GetChildren()
for MsgItem in MsgItems:
MsgDocker.append(WxUtils.SplitMessage(MsgItem))
return MsgDocker
@property
def GetLastMessage(self):
'''獲取當(dāng)前窗口中最后一條聊天記錄'''
uia.SetGlobalSearchTimeout(1.0)
MsgItem = self.MsgList.GetChildren()[-1]
Msg = WxUtils.SplitMessage(MsgItem)
uia.SetGlobalSearchTimeout(10.0)
return Msg
def LoadMoreMessage(self, n=0.1):
'''定位到當(dāng)前聊天頁面,并往上滾動鼠標滾輪,加載更多聊天記錄到內(nèi)存'''
n = 0.1 if n < 0.1 else 1 if n > 1 else n
self.MsgList.WheelUp(wheelTimes=int(500 * n), waitTime=0.1)
def SendScreenshot(self, name=None, classname=None):
'''發(fā)送某個桌面程序的截圖,如:微信、記事本...
name : 要發(fā)送的桌面程序名字,如:微信
classname : 要發(fā)送的桌面程序類別名,一般配合 spy 小工具使用,以獲取類名,如:微信的類名為 WeChatMainWndForPC'''
if name and classname:
return 0
else:
hwnd = win32gui.FindWindow(classname, name)
if hwnd:
WxUtils.Screenshot(hwnd)
self.SendClipboard()
return 1
else:
return 0
wxautoapi文章來源地址http://www.zghlxwxcb.cn/news/detail-650070.html
到了這里,關(guān)于【Python】通過第三方庫wxauto自動化操作微信電腦客戶端的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!