pywin32庫學習與簡單應用

使用pywin32庫實現簡單的接收和發送QQ消息

簡介

前段時間看到了這篇文章(使用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還是很實用的一個庫,後續再深入瞭解一下。

發佈了12 篇原創文章 · 獲贊 20 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章