python模拟键鼠,发送微信消息,支持命令行调用,可作为模块使用
本帖最后由 wkdxz 于 2022-8-13 10:05 编辑一直以来都是使用AHK来操作微信发消息的,最近看到@yuupuu 的帖子,看到python控制微信也比较稳定,而且写代码更简单,于是也参照原贴写了一个。
因为是在Excel里面使用代码,所以就做了参数调用,可以使用命令行运行。
为避免以后重复书写,就做成了一个简陋的模块,可以直接import调用。
命令行不方便传入多行文字,于是增加了直接发送剪贴板的功能。
因为是使用剪贴板来复制粘贴输入文字和文件,所以增加了还原剪贴板数据的功能。
2022-08-13更新:不再强制修改微信窗口大小和位置,使用相对坐标点击。
代码功能是东拼西凑的,不够精简和完善,喜欢的朋友可以拿去用。
这里的0.25是鼠标点击的延迟 0.25秒,如果电脑反应慢,可以增加这个数值。
def click(self, x, y, timeout=0.25):
除了主代码,另外两个是示例文件,喜欢的朋友可以下载来用。
主代码:wx_sender.py
from pyperclip import copy, paste
from time import sleep
import ctypes
import pyautogui
import sys
import win32clipboard
import win32con
import win32gui
class DROPFILES(ctypes.Structure):
_fields_ = [
("pFiles", ctypes.c_uint),
("x", ctypes.c_long),
("y", ctypes.c_long),
("fNC", ctypes.c_int),
("fWide", ctypes.c_bool),
]
def restore_clipboard(func):#自动还原剪贴板
def wrapper(*args, **kwargs):
clip_bak = paste()# 备份
result = func(*args, **kwargs)
copy(clip_bak)#还原
return result
return wrapper
class WxMsg:
def __handle(self):
return win32gui.FindWindow('WeChatMainWndForPC', '微信')
def __pos_and_size(self):
win_x, win_y, win_right_down_x, win_right_down_y = win32gui.GetWindowRect(
self.__handle())
win_width = win_right_down_x - win_x
win_height = win_right_down_y - win_y
return win_x, win_y, win_width, win_height
#复制文件到剪贴板 代码来源:https://www.cnblogs.com/love-DanDan/p/15900159.html
def __copy_file(self, files):
pDropFiles = DROPFILES()
pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
pDropFiles.fWide = True
mdata = bytes(pDropFiles)
files = ("\0".join(files)).replace("/", "\\")
data = files.encode("U16") + b"\0\0"
win32clipboard.OpenClipboard()
try:
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_HDROP,
mdata + data)
finally:
win32clipboard.CloseClipboard()
#打开窗口代码来源: 【yuupuu】 https://www.52pojie.cn/thread-1673429-1-1.html
def __activate_wx_window(self):
if handle := self.__handle():
win_x, win_y, win_width, win_height = self.__pos_and_size()
win32gui.ShowWindow(handle, win32con.SW_SHOWMINIMIZED)
win32gui.ShowWindow(handle, win32con.SW_SHOWNORMAL)
win32gui.ShowWindow(handle, win32con.SW_SHOW)
win32gui.SetWindowPos(handle, win32con.HWND_TOP, win_x, win_y,
win_width, win_height,
win32con.SWP_SHOWWINDOW)
win32gui.SetForegroundWindow(handle)
else:
print('微信未登录')
exit()
#复制文字,并粘贴
@restore_clipboard
def __wx_input(self, txt):
copy(txt)
pyautogui.hotkey('ctrl', 'v')
#延时点击坐标
#默认使用相对坐标点击 (实际点击位置:窗口坐标+相对坐标)
def __click(self, x, y, timeout=0.25, relative=True):#默认使用相对坐标点击
sleep(timeout)
win_x, win_y = self.__pos_and_size()[:2]
if relative:
pyautogui.click(win_x + x, win_y + y)#使用相对坐标点击
else:
pyautogui.click(x, y)
#把消息发送出去
def __send_msg_out(self):
sleep(0.4)
pyautogui.hotkey('alt', 's')
#搜索联系人
def search(self, wxid):
self.__activate_wx_window()
self.__click(21, 145)# 通讯录
self.__click(143, 39)# 搜索框
self.__wx_input(wxid)# 搜索微信id
self.__click(155, 120, 0.6)# 点第一个搜索结果 要多停留一会儿
#点输入框,从微信窗口右下角:x-50,y-63,使用绝对坐标点击
win_right_down_x, win_right_down_y = win32gui.GetWindowRect(
self.__handle())[-2:]#微信窗口右下角的坐标
self.__click(win_right_down_x - 50, win_right_down_y - 63, 0.2, False)
#发送文字消息
@restore_clipboard
def send_txt(self, txt, send_out=True):
copy(txt)# 粘贴文本内容
pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
if send_out:
self.__send_msg_out()
#发送文件,传入参数为文件路径
@restore_clipboard
def send_file(self, file_list, send_out=True):
if isinstance(file_list, str):
file_list =
self.__copy_file(file_list)
pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
if send_out:
self.__send_msg_out()
#直接发送剪贴板内容
def send_clipboard(self, send_out=True):
if paste():
pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
if send_out:
self.__send_msg_out()
else:
print('剪贴板为空')
exit()
if __name__ == '__main__':
arg_count = len(sys.argv)#传入的参数个数
if arg_count not in {1, 2, 3, 4}:
print('参数个数不是2或3,看看是不是传入了含空格的文件路径')
else:
wx = WxMsg()#实例化对象
# 3个参数发文件
if arg_count == 4:# 传入3个参数(微信id,消息内容,文件路径)
_, wxid, msg_content, msg_type = sys.argv
if msg_type.lower() == 'f':
wx.search(wxid)#搜索微信号
wx.send_file(msg_content)# 发送1个文件
else:
print(f'第3参数【{msg_type}】错误,必须是“f”\n可以检查下是不是传入了含空格的文件路径')
# 2个参数发文字
elif len(sys.argv) == 3:# 传入2个参数(微信id,文字消息内容)
_, wxid, msg_content = sys.argv
wx.search(wxid)#搜索微信号
wx.send_txt(msg_content)
# 1个参数发剪贴板内容
elif len(sys.argv) == 2:# 传入1个参数(微信id)
_, wxid = sys.argv
wx.search(wxid)#搜索微信号
wx.send_clipboard()
################################ 不传入参数,直接在python里运行,可以发送多行文本或者多个文件
elif arg_count == 1:
wxid = '小号'#要发送的微信号
wx.search(wxid)#搜索微信号
# 发送1条消息
wx.send_txt('测试1条消息')
# # 发送1个文件
# wx.send_file(r'C:\Windows\write.exe')
# # 发送多个文件,传入[文件路径列表]
# sleep(1)
# wx.send_file()
# # 发送多条文字消息
# sleep(1)
# for i in range(5):
# wx.send_txt(f'发送第{i+1}条文字消息')
# sleep(1)
# #粘贴消息,不发送出去
# wx.send_txt('此消息不会发送出去,因为添加了第二参数 False', False)
使用Python可以轻松调用
from wx_sender import WxMsg
from time import sleep
wx = WxMsg()#实例化对象
wxid = '小号'#要发送的微信号
wx.search(wxid)#搜索微信号
# 发送剪贴板内容
wx.send_clipboard()
# 发送1条消息
wx.send_txt('测试单条消息')
# 发送1个文件
wx.send_file(r'C:\Windows\write.exe')
# 发送多个文件,传入[文件路径列表]
sleep(1)
wx.send_file()
# 发送多条文字消息
sleep(1)
for i in range(5):
wx.send_txt(f'发送第{i+1}条文字消息')
sleep(1)
#粘贴消息,不发送出去
wx.send_txt('此消息不会发送出去,因为添加了第二参数 False', False)
使用命令行调用
@echo off & pushd %~dp0
set wxid=小号
::发送文字消息
.\wx_sender.py %wxid% 测试消息
echo 按任意键测试发送文件&pause>nul
::发送文件,在后面加上参数f
.\wx_sender.py %wxid% "C:\Windows\write.exe" f
echo 按任意键测试发送剪贴板&pause>nul
::发送剪贴板内容
.\wx_sender.py %wxid%
本帖最后由 wkdxz 于 2022-8-12 17:35 编辑
放羊的狼 发表于 2022-8-12 17:01
看楼主说的是在Excel中使用,那...sendkeys不一样能搞定吗,前阵子写过一个目前一直在用,貌似比较稳定,文 ...
正好VBA,python和AHK 我都在用。
vba别的不说,就延时一点就很卡,操作过程一直转圈,容易崩溃,可能是我技术不行或者WPS软件的问题。
AHK可以使用相对坐标点击,不用把微信窗口固定大小,放到左上角,但代码太不严谨,可参照的示例也没有python丰富。
python虽然要把微信窗口固定大小,放到左上角,但代码写起来很灵活,也有大堆的库可以调用。
后两者都可以使用cmd调用。所以,使用AHK或python书写,我还可以使用AHK快捷键来搜索联系人和发送微信消息。 太棒了,这个要被套娃了 太棒了,研究研究 这种坐标的 如果不是绝对坐标的 估计到时候可能会出错 玩wx这种东西还是得靠hook 这个有啥好的应用场景吗{:1_904:} JiaoDaoNiu 发表于 2022-8-12 16:45
这个有啥好的应用场景吗
微信啊,已经没有api了,用这个模拟发消息很不错 看楼主说的是在Excel中使用,那...sendkeys不一样能搞定吗,前阵子写过一个目前一直在用,貌似比较稳定,文本、@成员、文件都可以通过剪贴板处理。前几天测试过ahk并不稳定(可能是我比较菜,毕竟只了解了两三天),python不了解,但看你代码也是模拟按键,貌似绕路绕得有点远。 怎么运行呀 这个要一直在前台运行吧?