用Python做一个基于OCR的微信聊天机器人
该脚本为博主个人开发使用,当前已满足自身需要,是低配版的聊天机器人,为了节约您的时间,这里先把该脚本原理以及缺点说一下,看完再自行决定是否往下看正文.
原理:
- 利用微信PC客户端的聊天窗口截图,进行文字识别
- 识别到有新消息,将聊天内容发给聊天机器人接口
- 模拟鼠标键盘操作,在聊天窗口粘贴接口返回的对话内容,进行发送
缺点
- 仅支持单个窗口的聊天对话,也就是同一时间只能和一个好友或者群组对话
- 会模拟鼠标键盘的操作,所以在脚本启动的时候,尽量不要操作电脑
- 聊天窗口需要在屏幕上固定的位置显示,并且不能被其他窗口遮挡
- 仅支持文本消息,并且文本消息内容不能过长
小优点
- 只要页面符合微信的聊天,理论上可以用于任何的聊天窗口,原理上可行,但是未做验证
以下是正文
前几年了解到在Python上有一个叫itchat的项目,就是利用wechatpy和图灵机器人的接口结合,实现一个可以自动回复微信好友消息或者群组消息的机器人,机器人支持天气,笑话,百科,闲聊之类的对话,之前自己照着教程集成实现了可以自动对话的功能,感觉还蛮有趣的,不过当时玩了一阵子就没有再继续接触了.
过了两年后的今天,想再次用itchat实现聊天机器人的时候,发现了几个问题,
一个是我的微信号被限制了WEB网页端登录,同事的微信号登录网页版是正常的,而我的号登录的时候却提示:
为了你的帐号安全,此微信号不能登录网页微信。你可以使用Windows微信或Mac微信在电脑端登录。Windows微信下载地址:https://pc.weixin.qq.com Mac微信下载地址:https://mac.weixin.qq.com
另一个是图灵机器人的免费调用次数变少了,以前我记得是几百次一天的,现在未认证的不给调用了,认证了一天也只有可怜的100次/天
简而言之就是一句话:wechatpy我用不了了,于是自己想了其他的办法,花了四个小时做了一个属于自己开发的微信机器人,该脚本理论上不仅限于微信,应该还能用在其他聊天窗口,这里未做验证
原理
- 进行屏幕截图将微信PC端好友聊天的话泡处提取出来,进行判断是否有新的消息,截取的区域如下图所示,注意截图所示的红框区域的左上角和右下角
- 其中红框处的左上角坐标是是截图的起始坐标,也是判断是否有收到消息的判断方式,因为文字识别调用的是百度提供的服务,有一定的次数限制,一天免费额度5000次,虽然够用,但是能减少系统消耗就尽量减少消耗,所以这里根据进行了判断是否有收到好友消息。利用了聊天窗口的背景色,我的客户端背景色是RGB(245,245,245),所以当有好友发送消息的时候,这里会被好友头像挡住,大概率出现RGB不是(245,245,245)的情况,可以利用这一特点进行判断是否收到好友消息
- 上图红框处故意将框的长度延长了一点,是为了尽量能读取到收到的长文本消息。之后就将红框截图保存成文件,然后调用百度的OCR接口,上传识别。
截图使用的是PIL库,安装:
pip install pillow
截图保存相关代码:
from PIL import ImageGrab
from PIL import Image
#微信头像部分起始像素
wechatHeadPx = 1350
wechatHeadPy = 893
#话泡最长度底部像素
wechatMsgEndPx = 1686
wechatMsgEndPy = 920
# 截图保存,输入屏幕左上角和右下角的坐标
pic = ImageGrab.grab(bbox=(wechatHeadPx, wechatHeadPy, wechatMsgEndPx, wechatMsgEndPy))
pic.save(saveFileName)
判断像素是否相同代码:
piex = pic.getpixel((0,0))#获取像素点值,返回(37, 37, 38)
wechatEmptyPiex = (245,245,245)#微信聊天界面空白处的像素值
if piex!=wechatEmptyPiex:
#有收到好友消息,该处是会被头像挡住的点
pass
OCR的api相关链接,(APPID,apikey,SECRET_KEY自行申请获取)
安装百度OCR:
pip install baidu-aip
图片识别代码:
#百度ai
from aip import AipOcr
""" 读取图片 """
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
""" 你的 APPID AK SK """
BAIDU_OCR_APP_ID = '123456'
BAIDU_OCR_API_KEY = '123456'
BAIDU_OCR_SECRET_KEY = '123456'
client = AipOcr(BAIDU_OCR_APP_ID, BAIDU_OCR_API_KEY, BAIDU_OCR_SECRET_KEY)
saveFileName = "1.jpg"
image = get_file_content(saveFileName)
bs = client.basicGeneral(image);
print(bs)
上面代码关键key自行替换为自己申请的
- 获取到识别结果之后,就需要进行调用聊天机器人接口了,在这个步骤我了解了一些各种聊天机器人,最终决定使用百度的UNIT,功能还蛮强大的,方便拓展.当然现在完全够用了,各位可以使用其他的也行。
目前该应用免费使用,这里需要注意的是,申请了之后需要添加一些技能才行,我就添加了天气,百科,闲聊这些基本的。还有就是要去技能那里看看,找到闲聊对应的技能ID,后面要用。
聊天相关代码:
BAIDU_UNIT_API_KEY = "123456"
BAIDU_UNIT_SECRET_KEY = "123456"
#下面要替换成自己的bot_id,是你的技能ID!!,这里我用的是闲聊技能ID
bot_id='123456'
def getBaiDuAK():
# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+BAIDU_UNIT_API_KEY+"&client_secret="+BAIDU_UNIT_SECRET_KEY
r = requests.get(host)
return r.json()['access_token']
def baiduApi(text):
global access_token
url = 'https://aip.baidubce.com/rpc/2.0/unit/bot/chat?access_token=' + access_token
query = text
#下面的log_id在真实应用中要自己生成,可是递增的数字
log_id ='7758521'
#下面的user_id在真实应用中要是自己业务中的真实用户id、设备号、ip地址等,方便在日志分析中分析定位问题
user_id='222333'
post_data = '{\"bot_session\":\"\",\"log_id\":\"'+log_id+'\",\"request\":{\"bernard_level\":1,\"client_session\":\"{\\\"client_results\\\":\\\"\\\", \\\"candidate_options\\\":[]}\",\"query\":\"' + query + '\",\"query_info\":{\"asr_candidates\":[],\"source\":\"KEYBOARD\",\"type\":\"TEXT\"},\"updates\":\"\",\"user_id\":\"'+user_id+'\"},\"bot_id\":'+bot_id+',\"version\":\"2.0\"}'
print (json.loads(post_data))
headers = {'Content-Type':'application/json'}
r = requests.post(url, data=post_data.encode('utf-8'),headers=headers)
responseJson = r.json()['result']['response'];
action_list = responseJson[ 'action_list' ]
print ( action_list )
if len(action_list)>0:
return action_list[random.randint(0,len(action_list)-1)]['say']
res = baiduApi( "你好啊!" )
print( res )
- 将结果返回给好友
这里利用的是模拟鼠标键盘操作,首先是将文本结果复制到剪贴板,然后模拟鼠标点击消息输入区域,使其焦点在输入框,再模拟粘贴操作将文本粘贴上去,最后模拟回车发送操作
需要用到的库
import pyperclip #python实现复制粘贴
import pymouse,pykeyboard
from pymouse import *
from pykeyboard import PyKeyboard
pip install pyperclip
pip install PyKeyboard
注意
PyUserInput模块
安装该库好像需要先装PyHook,直接用pip命令还安装不上,所以需要下载文件来安装。
相关代码:
myMsgWindowHeadD = 1000
res = "聊天机器人返回结果"
print( res )
pyperclip.copy( res )
#点击
myMouse = PyMouse()
myMouse.click(wechatHeadPx,wechatHeadPy + myMsgWindowHeadD)
pasteText()
k = PyKeyboard()
k.press_key(k.enter_key) #回车键
上面点击区域是用图片中红框左上角往下移动1000像素,因为好友头像处的1000像素正下方刚好是我PC端的消息输入窗口,所以模拟鼠标单击此处就可以让消息输入框获取焦点,再进行模拟Ctrl+V粘贴,回车动作,完成消息发送。
代码说明结束------------------------------
tip:
- 可以使用win+左,或者win+右使微信客户端刚好占满半屏,我这里用的是右半屏,所以好友的聊天位置是每次都固定的,(只要没双屏,换显示器)
- 上述步骤完成之后,可以将窗口顶部往下拉,调的小一点都无所谓,这样占用的区域就不会太大了。
完整代码
部分代码需要替换成自己的key,像素位置信息需要自己通过截屏获取区域像素点,每台电脑情况不一样,参数设置完成后,才可以完成功能
import requests
from PIL import ImageGrab
from PIL import Image
import pytesseract
import pyperclip #python实现复制粘贴
import pymouse,pykeyboard,os,sys
from pymouse import *
from pykeyboard import PyKeyboard
import requests
import json
import random
import time
#百度ai
from aip import AipOcr
saveFileName = "1.jpg"
wechatEmptyPiex = (245,245,245)#微信聊天界面空白处的像素值
#微信头像部分起始像素
wechatHeadPx = 1350
wechatHeadPy = 893
#话泡最长度底部像素
wechatMsgEndPx = 1686
wechatMsgEndPy = 920
#我的聊天窗口和好友头像的像素差距
myMsgWindowHeadD = 1000
""" 你的 APPID AK SK """
BAIDU_OCR_APP_ID = '123456789'
BAIDU_OCR_API_KEY = '123456789'
BAIDU_OCR_SECRET_KEY = '123456789'
client = AipOcr(BAIDU_OCR_APP_ID, BAIDU_OCR_API_KEY, BAIDU_OCR_SECRET_KEY)
#百度unit参数
BAIDU_UNIT_API_KEY = "123456789"
BAIDU_UNIT_SECRET_KEY = "123456789"
#下面要替换成自己的bot_id,是你的技能ID!!,这里我用的是闲聊技能ID
bot_id='123456789'
def getBaiDuAK():
# client_id 为官网获取的AK, client_secret 为官网获取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+BAIDU_UNIT_API_KEY+"&client_secret="+BAIDU_UNIT_SECRET_KEY
r = requests.get(host)
return r.json()['access_token']
def baiduApi(text):
global access_token
url = 'https://aip.baidubce.com/rpc/2.0/unit/bot/chat?access_token=' + access_token
query = text
#下面的log_id在真实应用中要自己生成,可是递增的数字
log_id ='7758521'
#下面的user_id在真实应用中要是自己业务中的真实用户id、设备号、ip地址等,方便在日志分析中分析定位问题
user_id='222333'
post_data = '{\"bot_session\":\"\",\"log_id\":\"'+log_id+'\",\"request\":{\"bernard_level\":1,\"client_session\":\"{\\\"client_results\\\":\\\"\\\", \\\"candidate_options\\\":[]}\",\"query\":\"' + query + '\",\"query_info\":{\"asr_candidates\":[],\"source\":\"KEYBOARD\",\"type\":\"TEXT\"},\"updates\":\"\",\"user_id\":\"'+user_id+'\"},\"bot_id\":'+bot_id+',\"version\":\"2.0\"}'
print (json.loads(post_data))
headers = {'Content-Type':'application/json'}
r = requests.post(url, data=post_data.encode('utf-8'),headers=headers)
responseJson = r.json()['result']['response'];
action_list = responseJson[ 'action_list' ]
print ( action_list )
if len(action_list)>0:
return action_list[random.randint(0,len(action_list)-1)]['say']
""" 读取图片 """
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
"""模拟键盘粘贴操作"""
def pasteText():
k = PyKeyboard()
#模拟键盘点击ctrl+v
k.press_key(k.control_key)
k.tap_key('v')
k.release_key(k.control_key)
time.sleep(1)#速度别太快
global access_token #在使用前初次声明
access_token =getBaiDuAK()
print( "\n" )
lastMsg = "";#上一次消息
def getLastMsgStr():#运行一次截取消息并且进行识别
# 截图保存,输入屏幕左上角和右下角的坐标
pic = ImageGrab.grab(bbox=(wechatHeadPx, wechatHeadPy, wechatMsgEndPx, wechatMsgEndPy))
pic.save(saveFileName)
piex = pic.getpixel((0,0))#获取像素点值,返回(37, 37, 38)
if piex!=wechatEmptyPiex:
#说明不是空消息,有获取到头像像素信息
image = get_file_content(saveFileName)
""" 调用通用文字识别, 图片参数为本地图片 """
bs = client.basicGeneral(image);
print(bs)
print("图片识别结果数量" + str( bs["words_result_num"] ))
if bs["words_result_num"]>0:#识别到有结果
resultSet = bs["words_result"]
resultStr = resultSet[0]["words"]#识别到的文字
print("识别到聊天的文字为:"+ resultStr )
global lastMsg
#跟之前的聊天消息进行对比,看看是不是重复了
if lastMsg is not resultStr:
lastMsg = resultStr#刷新最后消息
return resultStr
while True:
strLastMsg = getLastMsgStr()
if strLastMsg is not None:
print("返回识别文字为:"+ str( strLastMsg ))
res = baiduApi( strLastMsg )
print( res )
pyperclip.copy( res )
#点击
myMouse = PyMouse()
myMouse.click(wechatHeadPx,wechatHeadPy + myMsgWindowHeadD)
pasteText()
k = PyKeyboard()
k.press_key(k.enter_key) #回车键
time.sleep(5)#速度别太快