pywin32库学习与简单应用

使用pywin32库实现简单的接收和发送QQ消息

    • 简介
    • 准备
    • 接收QQ消息
    • 发送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还是很实用的一个库,后续再深入了解一下。

你可能感兴趣的:(python常用第三方库,python,windows)