前段时间看到了这篇文章(使用python发送qq消息),是用pywin32实现,感觉很实用,就想学习一下这个库,并实现简单的接收和发送QQ消息(其实就是模拟鼠标键盘行为),这里简单记录一下。pywin32库安装好后里面会有一个.chm的帮助文档,有各个模块的作用和用法。完整代码点击下载。
1.使用python3.6
2.pywin32库
首先,需要单独打开对方的QQ聊天界面,再打开和代码同一目录下名为tmp.txt的记事本,程序会首先打开这两个窗口,设置在固定的位置,并设置大小。
def setwindowspos(to_who):
#设置两个窗口的大小和位置,失败返回False,成功返回True
qq_window = win32gui.FindWindow(None, to_who)
if qq_window == 0:
return False
win32gui.ShowWindow(qq_window, 1)
win32gui.SetWindowPos(qq_window, win32con.HWND_TOPMOST, 50,50,650,550, win32con.SWP_SHOWWINDOW)
tmptxt_window = win32gui.FindWindow(None, 'tmp.txt - 记事本')
if qq_window == 0:
return False
win32gui.ShowWindow(tmptxt_window, 1)
win32gui.SetWindowPos(tmptxt_window, win32con.HWND_TOPMOST, 180, 100, 650, 550, win32con.SWP_SHOWWINDOW)
return True
模拟鼠标和键盘的函数
def leftclick():
#模拟鼠标左键点击,自带延迟0.2s
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN | win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
time.sleep(0.2)
def rightclick():
#模拟鼠标右键点击,自带延迟0.2s
win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN | win32con.MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
time.sleep(0.2)
def presskeyboard(key_str):
#模拟按键盘,自带延迟0.5s
#keybd_event函数的第一个参数是按键的代码
win32api.keybd_event(keyboard_table[key_str], 0, 0, 0)
win32api.keybd_event(keyboard_table[key_str], 0, win32con.KEYEVENTF_KEYUP, 0)
time.sleep(0.5)
初始化函数
def initmain():
#完成keyboard_table的赋值
alphabet = "abcdefghijklmnopqrstuvwxyz"
for i in range(65, 91):
keyboard_table[alphabet[i-65]] = i
keyboard_table['shift'] = 16
keyboard_table['ctrl'] = 17
keyboard_table['alt'] = 18
keyboard_table['enter'] = 13
利用ctypes库的windll判断指定一点的颜色,来确定是否有新消息。对方没有发消息那这点就是背景的白色,否则就是聊天框的背景色或字的颜色。
def checknewmessage():
#根据像素值变化,判断是否有新消息
gdi32 = windll.gdi32
user32 = windll.user32
#获取句柄
hdc = user32.GetDC(None)
#获取指定像素的颜色
c = gdi32.GetPixel(hdc,120,425)
#TIM的背景色是白色0xffffff
if c == 16777215:
return False
return True
如果有新消息,则在指定位置模拟鼠标右键和键盘C键,完成消息复制,并粘贴到tmp.txt里。然后读取tmp.txt内容,做出相应的回复。
def main():
initmain()
STATUS = True
#判断窗口是否存在,存在则设置两个窗口的大小和位置
if setwindowspos(to_who) == False:
print("No such window.")
return False
while STATUS:
qq_window = win32gui.FindWindow(None, to_who)
win32gui.ShowWindow(qq_window, 1)
win32api.SetCursorPos((120, 425))
#检查新消息
if checknewmessage() == False:
print("No new message.")
time.sleep(3)
continue
#复制QQ接收到的消息
rightclick()
presskeyboard('c')
#将QQ消息复制到本地tmp.txt并保存
tmptxt_window = win32gui.FindWindow(None, 'tmp.txt - 记事本')
win32gui.ShowWindow(tmptxt_window, 1)
win32api.SetCursorPos((750, 425))
leftclick()
rightclick()
presskeyboard('a')
rightclick()
presskeyboard('p')
presskeyboard('alt')
presskeyboard('f')
presskeyboard('s')
#读取本地tmp.txt并进行相应操作
f = open('tmp.txt', 'r', encoding = 'UTF-8')
r = f.read()
f.close()
text = str(r.encode('UTF-8'), encoding = "UTF-8").replace('\n', '')
text.replace(' ', '')
print(text)
if text == "exit":
print("End interaction.")
break
elif len(text) > 25:
print("Text is too long.")
continue
else:
print("Wait for reply.")
replymessage(to_who, text)
time.sleep(1)
程序会根据不用的消息,做出相应回复。
def replymessage(to_who, msg):
#根据接收内容进行不同的回复
global act
#发送setu
if msg[0:4] == "setu":
sendmessage(to_who, "发送中...")
sendimage(to_who, int(msg[4:len(msg)]))
#切换API
elif msg[0:3] == "act":
act = "act" + msg[3:len(msg)]
sendmessage(to_who, "切换功能完成")
#使用腾讯API进行回复
elif act == "act1":
reply_content = tencentapi(msg)
sendmessage(to_who, reply_content)
#使用青云客API进行回复
elif act == "act2":
reply_content = qingyunkeapi(msg)
sendmessage(to_who, reply_content)
如果接收到"setu+数字“(如setu3),则从某个网站上下载相应数量的图片发送过去。这里用到requests和BeatuifulSoup库,可以看看我的上篇博客,点击这里。
def downloadimage(img_num):
#下载图片
img_num = min(img_num, 20)
se_url = "http://xxxxxxxxxxxx"
main_req = requests.get(se_url)
main_html = main_req.text
main_soup = BeautifulSoup(main_html, "lxml")
cnt = 0
for img in main_soup.find_all('img', class_="lazy viewer"):
img_url = img.get('data-original')
get_img = requests.get(img_url)
img_name = 'tmp' + str(cnt) + '.jpg'
with open(img_name, 'wb') as f:
f.write(get_img.content)
f.close()
try:
Image.open(img_name).verify()
except Exception as e:
print("Error", e)
break
old_img = Image.open(img_name)
new_img = 'tmp' + str(cnt) + '.bmp'
old_img.save(new_img)
cnt += 1
if cnt >= img_num:
break
return min(cnt, img_num)
def sendimage(to_who, download_num):
#发送图片
cur_num = downloadimage(download_num)
for i in range(cur_num):
#按序将图片写进剪切板里,然后将剪切板内容发送到QQ窗口,并模拟按下Enter键
tmp_img_path = "tmp" + str(i) + ".bmp"
img = windll.user32.LoadImageW(0, tmp_img_path, win32con.IMAGE_BITMAP, 0, 0, win32con.LR_LOADFROMFILE)
setimagetoclipboard(img)
qq_window = win32gui.FindWindow(None, to_who)
win32gui.SendMessage(qq_window, 770, None, None) #这里770应该是剪切板ID
win32gui.SendMessage(qq_window, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
win32gui.SendMessage(qq_window, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
上面用的setimagetoclipboard函数
def setimagetoclipboard(img):
#复制图片到剪切板
cb.OpenClipboard()
cb.EmptyClipboard()
cb.SetClipboardData(win32con.CF_BITMAP, img)
cb.CloseClipboard()
def getimagefromclipboard(hwnd):
#获取剪切板图片
cb.OpenClipboard(hwnd)
cb_img = cb.GetClipboardData(win32con.CF_BITMAP)
cb.CloseClipboard()
return cb_img
如何接收到"act+数字"(如act2),则切换使用的API。这里用到的是腾讯智能闲聊和青云客,这里有篇文章介绍如何使用腾讯API,腾讯AI开放平台里面还有很多API可以用,改一下链接和参数就可以了。
def sendmessage(to_who, msg):
#发送文本信息
print(msg)
setmessagetoclipboard(msg)
qq_window = win32gui.FindWindow(None, to_who)
win32gui.ShowWindow(qq_window, 1)
win32api.SetCursorPos((120, 545))
rightclick()
presskeyboard("p")
presskeyboard("enter")
def tencentapi(content):
#使用腾讯聊天API
api_url = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat'
content_plus = content.encode('UTF-8')
payload = md5sign.getparams(content_plus)
cnt = 0
r = requests.post(api_url, data=payload)
while cnt < 5 and str(r) == "":
r = requests.post(api_url, data=payload)
cnt += 1
return r.json()['data']['answer']
def qingyunkeapi(content):
#使用青云客的API
api_url = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=" + content
cnt = 0
r = requests.get(api_url)
while cnt < 5 and str(r) == "":
r = requests.get(api_url)
cnt += 1
return r.json()['content']
上面用的setmessagetoclipboard函数
def setmessagetoclipboard(msg):
#复制文本到剪切板
cb.OpenClipboard()
cb.EmptyClipboard()
cb.SetClipboardData(win32con.CF_UNICODETEXT, msg)
cb.CloseClipboard()
def getmessagefromclipboard(hwnd):
#获取剪切板文本
cb.OpenClipboard(hwnd)
cb_msg = cb.GetClipboardData(win32con.CF_UNICODETEXT)
cb.CloseClipboard()
return cb_msg
上面贴出来的代码只是部分,完整代码可以从开头链接处下载。这里主要用到了win32con,win32gui,win32api,win32clipboard等几个模块。感觉pywin32还是很实用的一个库,后续再深入了解一下。