簡介
前段時間看到了這篇文章(使用python發送qq消息),是用pywin32實現,感覺很實用,就想學習一下這個庫,並實現簡單的接收和發送QQ消息(其實就是模擬鼠標鍵盤行爲),這裏簡單記錄一下。pywin32庫安裝好后里面會有一個.chm的幫助文檔,有各個模塊的作用和用法。完整代碼點擊下載。
準備
1.使用python3.6
2.pywin32庫
接收QQ消息
首先,需要單獨打開對方的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)
發送QQ消息
程序會根據不用的消息,做出相應回覆。
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還是很實用的一個庫,後續再深入瞭解一下。