本文简介
好久没更新文章啦,因为最近在赶一本Py的入门书,碰巧今天把这篇文章赶出来了。
而很多加群的小朋友很多都是咨询如何搭建微信机器人的,所以就把这一章放出来了,
取需,三个实用示例:定时发信息,集成图灵API实现聊天机器人,微信防撤回实现,基本够玩了。
另外,微信改版了网页端,很多接口都开始用不了,比如拉人进群,可以通过无障碍服务或者
Xposed来解决,具体怎么玩可以参见前面的章节,谢谢。
2011年1月21日,微信推出第一个正式版本,到现在已有7个年头。从一开始的不被看到好,到现在的用户量超10亿,大众的日常生活越来越离不开微信。人生苦短我用Python,有没有办法通过Python来对我们使用微信提供一些便利呢? 答案肯定是有的,在Github上有一个基于微信网页版接口微信开源库:itchat,通过几十行的代码就能轻松实现一个微信机器人。本章我们就来了解学习这个库,然后通过三个实用案例来帮大家玩转这个库。
我们跟着文档来解读下itchat这个库的用法。
itchat的仓库地址:https://github.com/littlecodersh/ItChat
官方文档:http://itchat.readthedocs.io
安装也很简单,直接通过pip命令安装即可,命令如下:
pip install itchat
通过一个简单的例子来让读者体会下通过itchat编写一个微信机器人有多简单,代码功能:
扫码登录后给文件助手发送一条信息,监听接收到的文件信息,打印出来,具体代码如下。
import itchatimport time@itchat.msg_register(itchat.content.TEXT)def reply_msg(msg): print("收到一条信息:",msg.text)if __name__ == '__main__': itchat.auto_login() time.sleep(5) itchat.send("文件助手你好哦", toUserName="filehelper") itchat.run()
代码执行结果:
先会弹出一个二维码图片,扫描后会登陆网页端微信,间隔一会儿后文件助手收到如图19.1所示的信息
图19.1 文件传输助手收到信息
然后用另一个账号发送一条信息给这个账号,控制台会把接收到的信息打印出来
Getting uuid of QR code.Downloading QR code.Please scan the QR code to log in.Please press confirm on your phone.Loading the contact, this may take a little while.TERM environment variable not set.Login successfully as Robot PigStart auto replying.收到一条信息: 你好
通过上面8行有效代码就实现了一个简单的技巧人,酷不酷,带着这样的思路,我们可以进行扩展,比如添加一个自动回复的功能,比如在忙的时候,别人给你发信息,自动回复:”在忙,晚点给你回复信息”等。
在学习itchat的详细用法前,先和读者说一些要注意的东西。
(1)itchat不是微信官方提供的库,意味使用这个库会有风险,笔者的小号就曾被微信封过一段时间,禁止网页端登录微信,移动端和电脑客户端还是能正常使用的,微信此举旨在封杀泛滥的微商机器人。
(2)如何减少被封概率:消息发送不要过于频繁;不要发送过多重复信息;尽量少调用加人的接口;
(3)被封如何解封:被封后是无法找到申述入口,只能随缘解封,笔者在坚持了一周用手机聊天和朋友圈点赞留言后突然就解封了。
(4)微信正在慢慢收窄网页端的功能,意味着一些接口会慢慢失效,比如说拉人进群聊的接口,以前还能用,现在就不行了。在调用某个接口没有得到意料中的结果,可能就是接口失效了,可以到官方仓库查找相关的issues。
(5)微信只支持双端登录,不使用黑科技的话,移动端加上Web网页端或PC客户端。
(6)新注册的微信号是无法使用网页版登录的。
最后,还是要感谢开源作者的无私奉献。
巧妇难为无米之炊,在扩展前先跟笔者把文档过一过,理解得差不多了,再开始去扩展,这样效率会高很多。
登陆时通过itchat.auto_login()这个函数来完成的,不带参数的话会生成一个二维码图片文件供你扫描登陆。一般的话我们的电脑都会关机,如果机器人有需要持久在线的需求,我们可以把脚本挂到服务器上,24小时跑,但是一般的云服务器是没有界面的,都是通过终端命令行进行操作,这个时候可以添加enableCmdQR=True参数,让二维码显示到命令行上,另外部分系统可能字符宽度有出入,如图19.2所示,可以通过把enableCmdQR赋值为特定的倍数进行调整。
图19.2 命令行二维码错位
比如enableCmdQR=2后,二维码图片如图19.3所示
图19.3 调整后正常的二维码图片
扫码登录后,如果想退出程序以后还暂存登录状态,重新执行程序也不用扫码可以添加参数hotReload=True。
如果在启动时没有设置hotReload=True参数,程序退出后过一会儿就会自动掉线的了,如果想快速退出的话可以调用itchat.logout()注销登录状态。另外,有时我们可能想在登录成功或者注销登录后执行一些操作,可以添加两个调用登录时传入两个方法参数loginCallback和exitCallback,简单示例如下:
import itchatimport timedef after_login(): print("登录后调用")def after_logout(): print("退出后调用")if __name__ == '__main__': itchat.auto_login(loginCallback=after_login, exitCallback=after_logout) time.sleep(5) itchat.logout()
代码执行结果如下:
Getting uuid of QR code.Downloading QR code.Please scan the QR code to log in.Please press confirm on your phone.Loading the contact, this may take a little while.登录后调用退出后调用
itchat提供四种查找用户的搜索方式,
(1)获取自己的用户信息 示例如下:
# 获取自己的用户信息,返回自己的属性字典 result = itchat.search_friends() print(result)
代码执行结果如下:
{'MemberList': <ContactList: []>, 'UserName': '@299f59697878267efb48e8cad07xxxxcadd0efbb63xxxxxxx0964c51f028e8474', 'City': '', 'DisplayName': '', 'PYQuanPin': '', 'RemarkPYInitial': '', 'Province': '', 'KeyWord': '', 'RemarkName': '', 'PYInitial': '', 'EncryChatRoomId': '', 'Alias': '', 'Signature': '(´v`o)♡', 'NickName': 'Robot Pig', 'RemarkPYQuanPin': '', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=1663312400&username=@299f59697878267efb48e8cad07f5f1cadd0efbb63ae19610964c51f028e8474&skey=@crypt_2d4a1972_5e7829c893346a53135fb03affa39f9c', 'UniFriend': 0, 'Sex': 2, 'AppAccountFlag': 0, 'VerifyFlag': 0, 'ChatRoomId': 0, 'HideInputBarFlag': 0, 'AttrStatus': 0, 'SnsFlag': 1, 'MemberCount': 0, 'OwnerUin': 0, 'ContactFlag': 0, 'Uin': 3454488193, 'StarFriend': 0, 'Statues': 0, 'WebWxPluginSwitch': 0, 'HeadImgFlag': 1}
(2)根据昵称查询某个用户,代码示例如下:
# 根据姓名查找用户 result = itchat.search_friends(name='培杰') print(result)
代码执行结果如下:
[<User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@xxb096c3036543exx2d4de4fc222xxxx', 'NickName': '培杰', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@40b096c3036543e5b2d4de4fc22208ed&skey=@crypt_2d4a1972_ac0122b1740b332921afc9f2fffa546f', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': 'Expectation is the root of all heartache.', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'PJ', 'PYQuanPin': 'peijie', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>]
(3)根据微信号查找用户,代码示例如下:
# 根据微信号查找用户 result = itchat.search_friends(wechatAccount='zpj779878443') print(result)
代码执行结果如下:
[<User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@xxb096c3036543exx2d4de4fc222xxxx', 'NickName': '培杰', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@40b096c3036543e5b2d4de4fc22208ed&skey=@crypt_2d4a1972_ac0122b1740b332921afc9f2fffa546f', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': 'Expectation is the root of all heartache.', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'PJ', 'PYQuanPin': 'peijie', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>]
另外,2和3功能可以同时使用,比如itchat.search_friends(name=’培杰’, wechatAccount=’zpj779878443’)
(4)根据UserName查找用户,就是上面返回结果里跟着的UserName字段,@xxxx这样一串东西,代码示例如下:
# 根据UserName查找用户 result = itchat.search_friends(userName='@xxb096c3036543exx2d4de4fc222xxxx') print(result)
代码执行结果如下:
[<User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@xxb096c3036543exx2d4de4fc222xxxx', 'NickName': '培杰', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@xxb096c3036543exx2d4de4fc222xxxx&skey=@crypt_2d4a1972_ac0122b1740b332921afc9f2fffa546f', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': 'Expectation is the root of all heartache.', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'PJ', 'PYQuanPin': 'peijie', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>]
itchat提供了几个发送不同类型信息的函数,没有发送语音的原因是网页版没有这个接口,可供调用
的函数如表19.1所示。
函数名 | 作用 |
---|---|
send_msg() | 发送文字信息 |
send_file() | 发送文件 |
send_video() | 发送视频 |
send_image() | 发送图片 |
使用代码示例如下:
import itchatimport timedef after(): user_info = itchat.search_friends(name='培杰') if len(user_info) > 0: # 拿到用户名 user_name = user_info[0]['UserName'] # 发送文字信息 itchat.send_msg('培杰你好啊!', user_name) # 发送图片 time.sleep(10) itchat.send_image('cat.jpg', user_name) # 发送文件 time.sleep(10) itchat.send_file('19_2.py', user_name) # 发送视频 time.sleep(10) itchat.send_video('sport.mp4', user_name)if __name__ == '__main__': itchat.auto_login(loginCallback=after) itchat.run()
代码执行结果如下:
除了主动发送信息外,还可以对信息进行监控,支持对多种类型的信息进行监控,类型如表19.2所示。
另外,有多个注册信息监听,后注册的信息优先级高于先注册信息,带参数信息高于不带参数信息。
信息类型 | 解释 |
---|---|
itchat.content.TEXT | 文本内容 |
itchat.content.MAP | 位置文本 |
itchat.content.Card | 名片 |
itchat.content.Note | 通知文本 |
itchat.content.Sharing | 分享名称 |
itchat.content.RECORDING | 录音 |
itchat.PICTURE | 图片/表情 |
itchat.content.VOICE | 录音 |
itchat.content.ATTACHMENT | 附件 |
itchat.content.VIDEO | 短视频 |
itchat.content.FRIENDS | 好友邀请 |
itchat.content.SYSTEM | 系统信息 |
一个监听到文字信息并响应信息的代码示例如下:
import itchat@itchat.msg_register(itchat.content.TEXT)def reply_msg(msg): if msg['Content'] == u'你好': itchat.send_msg(msg['User']['NickName'] + "你好啊!", msg['FromUserName'])if __name__ == '__main__': itchat.auto_login() itchat.run()
代码执行结果如下:
在微信网页端改版后,创建群聊、拉人进群和删除群聊这几个接口都用不了,现在利用itchat能做的有:查找群聊,往群聊发送信息,以及监控群聊信息,使用代码示例如下:
import itchatimport time@itchat.msg_register(itchat.content.TEXT, isGroupChat=True)def reply_msg(msg): print("收到一条群信息:", msg['ActualNickName'], msg['Content'])def after_login(): # 获得完整的群聊列表 print("完整的群聊列表如下:") print(itchat.get_chatrooms()) # 查找特定群聊 time.sleep(10) # 通过群聊名查找 chat_rooms = itchat.search_chatrooms(name='小猪的Python学习交流群') if len(chat_rooms) > 0: itchat.send_msg('测试', chat_rooms[0]['UserName'])if __name__ == '__main__': itchat.auto_login(loginCallback=after_login) itchat.run()
代码执行结果如下:
完整的群聊列表如下:[<Chatroom: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@@60dc5027bbbb83d532aa633b8d126szcf497a98ceea5c098d2c65f0932139b88', 'NickName': '湖北人在深圳90后', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgetheadimg?seq=625714901&username=@@60dc502769a783d532aa633b8d126190f497a98ceea5c098d2c65f0932139b88&skey=@crypt_2d4a1972_ea00536c8ac4e35fae1c2a1c48dfe40d', 'ContactFlag': 3, 'MemberCount': 82, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 0, 'Signature': '', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'HBRZSZ90H', 'PYQuanPin': 'hubeirenzaishenzhen90hou', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 0, 'Province': '', 'City': '', 'Alias': '', 'SnsFlag': 0, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': '', 'EncryChatRoomId': '', 'IsOwner': 0, 'IsAdmin': None, 'Self': <User: {'MemberList': <ContactList: []>, 'UserName': '@29b9cb6386352503319f411754e7424e383ae09e50a224feca754a4516db6a13', 'City': '', 'DisplayName': '', 'PYQuanPin': '', 'RemarkPYInitial': '', 'Province': '', 'KeyWord': '', 'RemarkName': '', 'PYInitial': '', 'EncryChatRoomId': '', 'Alias': '', 'Signature': '(´v`o)♡', 'NickName': 'Robot Pig', 'RemarkPYQuanPin': '', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=275167114&username=@29b9cb6386352503319f411754e7424e383ae09e50a224feca754a4516db6a13&skey=@crypt_2d4a1972_ea00536c8ac4e35fae1c2a1c48dfe40d', 'UniFriend': 0, 'Sex': 2, 'AppAccountFlag': 0, 'VerifyFlag': 0, 'ChatRoomId': 0, 'HideInputBarFlag': 0, 'AttrStatus': 0, 'SnsFlag': 1, 'MemberCount': 0, 'OwnerUin': 0, 'ContactFlag': 0, 'Uin': 3454488193, 'StarFriend': 0, 'Statues': 0, 'WebWxPluginSwitch': 0, 'HeadImgFlag': 1}>}> 内容过多省略... ]收到一条群信息: 培杰 123收到一条群信息: 培杰 你好
聊天记录截图:
另外,群聊除了通过群名搜索外还可以通过username来查找,或者两者搭配使用;
在msg里有一个isAt字段,可用于判断是否被人@了。
使用方式和群聊的非常类似,搜索公众号方法search_mps,监听公众号信息添加isMpChat=True元素,使用代码示例如下:
import itchat@itchat.msg_register(itchat.content.TEXT, isMpChat=True)def reply_msg(msg): print("收到一条公众号信息:", msg['User']['NickName'], msg['Content'])def login_after(): mps = itchat.search_mps(name='CoderPig') if len(mps) > 0: print(mps) itchat.send_msg('人生苦短', toUserName=mps[0]['UserName'])if __name__ == '__main__': itchat.auto_login(loginCallback=login_after) itchat.run()
代码执行结果如下:
[<MassivePlatform: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@07585e92f75be7320e49627cf0c3ad43', 'NickName': 'CoderPig', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=610904174&username=@07585e92f75be7320e49627cf0c3ad43&skey=@crypt_2d4a1972_bc443bf966f94fa11f2db8f812e456cf', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 0, 'Signature': '一枚咸鱼Android开发,会点Python,分享点学习经验,总结,鸡汤,读书笔记,生活技巧', 'VerifyFlag': 8, 'OwnerUin': 0, 'PYInitial': 'CODERPIG', 'PYQuanPin': 'CoderPig', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 0, 'Province': '广东', 'City': '深圳', 'Alias': '', 'SnsFlag': 0, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'gh_', 'EncryChatRoomId': '', 'IsOwner': 0}>]收到一条公众号信息: CoderPig 我用Python
聊天记录截图:
定时发送信息这个操作在日常生活中非常实用,比如给别人发生日或者节日祝福,晚上12点准时发,如果有早睡习惯的要熬到12点就很难受了,我们可以利用itchat编写一个简单的定时发送信息脚本。这里我们用到一个apscheduler定时调度任务模块,在命令行键入pip install apscheduler即可完成安装。这里并不会详细讲解,有兴趣的可移步到官方文档自行查阅:
http://apscheduler.readthedocs.io/en/latest/userguide.html
定时发送消息的代码示例如下:
import itchatfrom apscheduler.schedulers.blocking import BlockingSchedulerimport time# 发送信息def send_msg(): user_info = itchat.search_friends(name='培杰') if len(user_info) > 0: user_name = user_info[0]['UserName'] itchat.send_msg('生日快乐哦!', toUserName=user_name)def after_login(): sched.add_job(send_msg, 'cron', year=2018, month=7, day=28, hour=16, minute=5, second=30) sched.start()def after_logout(): sched.shutdown()if __name__ == '__main__': sched = BlockingScheduler() itchat.auto_login(loginCallback=after_login, exitCallback=after_login) itchat.run()
代码执行结果如下:
图灵机器人官网:http://www.tuling123.com/member/robot/index.jhtml
注册一个账号后,点击创建机器人,会弹出如图所示的面板,按需配置下即可。
普通账户可以创建5个机器人,每天有5000次的免费调用机会。点击新创建好的机器人,
会进入如图所示的界面,我们只需要保存下apikey,调用接口用的秘钥。
点击底下的api使用文档,或者直接打开 https://www.kancloud.cn/turing/web_api/522992,进入接口文档页,在这里我们可以看到接口调用相关的信息,包括接口地址:http://openapi.tuling123.com/openapi/api/v2,请求方式:POST,请求数据格式:JSON,请求数据示例:
{ "reqType":0, "perception": { "inputText": { "text": "附近的酒店" }, "inputImage": { "url": "imageUrl" }, "selfInfo": { "location": { "city": "北京", "province": "北京", "street": "信息路" } } }, "userInfo": { "apiKey": "", "userId": "" }}
我们可以利用Postman模拟下请求,试试接口是否可用,先设下请求头:
Content-Type:application/jsonHost:openapi.tuling123.comUser-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 Safari/537.36
接着是提交数据:
{ "reqType":0, "perception": { "inputText": { "text": "你好" } }, "userInfo": { "apiKey": "7e9377d760274b3499f6dec8eed37bbb", "userId": "123" }}
看下返回结果:
{ "emotion": { "robotEmotion": { "a": 0, "d": 0, "emotionId": 0, "p": 0 }, "userEmotion": { "a": 0, "d": 0, "emotionId": 0, "p": 0 } }, "intent": { "actionName": "", "code": 10004, "intentName": "" }, "results": [ { "groupType": 1, "resultType": "text", "values": { "text": "我很好,你也要好好的" } } ]}
返回结果里的text明显就是我们想要的东西,整个过程了解了,接着我们来编写代码,流程如下:
(1)监听微信信息。
(2)接收到信息,获取信息内容。
(3)调用接口,获取请求结果,提取返回的text。
(4)把提取到的text返回给发送信息的人。
具体代码实现如下:
import itchatimport requests as rq@itchat.msg_register(itchat.content.TEXT)def reply_msg(msg): info = msg['Content'].encode('utf8') # 图灵API接口 api_url = 'http://openapi.tuling123.com/openapi/api/v2' # 接口请求数据 data = { "reqType": 0, "perception": { "inputText": { "text": str(info) } }, "userInfo": { "apiKey": "7e9377d76fc7ee9499f6dec8eed37bbb", "userId": "123" } } headers = { 'Content-Type': 'application/json', 'Host': 'openapi.tuling123.com', 'User-Agent': 'Mozilla/5.0 (Wi`ndows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 ' 'Safari/537.36 ' } # 请求接口 result = rq.post(api_url, headers=headers, json=data).json() # 提取text,发送给发信息的人 itchat.send_msg(result['results'][0]['values']['text'], msg['FromUserName']) print()if __name__ == '__main__': itchat.auto_login() itchat.run()
代码执行结果如下图所示:
上面的图灵机器人有时回答得牛头不对马嘴,特别是在多轮问答的时候,闲聊玩玩还是不错的。
接着的我们要利用itchat来编写一个微信信息防撤回的脚本。当监控到用户或者群聊发送信息
撤回的话,把撤回的内容通过文件传输助手发送给我们,说下大体的思路流程。
(1)监听所有聊天记录,包括群聊,把信息都存入到一个字典里,资源类文件下载到本地。
(2)监听到撤回信息后,根据撤回的信息id,查找字典里对应的信息,发送给文件助手。
(3)每隔五分钟清理缓存数据。
流程看上去是挺简单,接着我们一点点来摸索实现。
先是监控信息,信息又分为好友聊天和群聊,我们编写代码来监控下收到的数据是怎么样的?
import itchatfrom itchat.content import *# 好友信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True)def handle_friend_msg(msg): print("好友信息: ", msg)# 群聊信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True)def information(msg): print("群聊信息: ", msg)if __name__ == '__main__': itchat.auto_login() itchat.run()
给机器人发送一条信息,还有往群聊里发送一条信息,查看打印的信息内容,先是好友信息
好友信息: {'MsgId': '5500935424291030814', 'FromUserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'ToUserName': '@913c3124d973db6ae25924bd0598b48a0028e0c2d01e18d8095cc6fd58db116b', 'MsgType': 1, 'Content': '123', 'Status': 3, 'ImgStatus': 1, 'CreateTime': 1533010285, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 5500935424291030814, 'OriContent': '', 'EncryFileName': '', 'User': <User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'NickName': 'CoderPig', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@8fd8b5b2bd0862ed5d0d573bc6c08362&skey=@crypt_2d4a1972_26dc3be99a177455b82518b3ca6e6cc5', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': '不服气,就用行动去证明,少说多做...2018.7.29', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'CODERPIG', 'PYQuanPin': 'CoderPig', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>, 'Type': 'Text', 'Text': '123'}
分析下要采集的数据,MsgId(微信信息的标识,用来作为键),NickName(发送者的用户名),Content(信息内容),CreateTime(创建时间), Type(信息类型)。接着是群聊信息:
群聊信息: {'MsgId': '3177606925001563512', 'FromUserName': '@@16521484d35b2fe9c953282d98ec4f11456607924b3a7cc6d7fb671fe7e3081c', 'ToUserName': '@913c3124d973db6ae25924bd0598b48a0028e0c2d01e18d8095cc6fd58db116b', 'MsgType': 1, 'Content': '嘿嘿', 'Status': 3, 'ImgStatus': 1, 'CreateTime': 1533010298, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 3177606925001563512, 'OriContent': '', 'EncryFileName': '', 'ActualNickName': '易♂建♂联', 'IsAt': False, 'ActualUserName': '@8fd8b5b2bd0862ed5d0d573bc6c08362', 'User':
同样分析下要采集的数据,MsgId(微信信息的标识),ActualNickName(发送者群名称),Content(信息内容),CreateTime(创建时间), Type(信息类型)。改下我们的程序,把这些都提取打印出来。
import itchatfrom itchat.content import *# 好友信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True)def handle_friend_msg(msg): msg_id = msg['MsgId'] msg_from_user = msg['User']['NickName'] msg_content = msg['Content'] msg_create_time = msg['CreateTime'] msg_type = msg['Type'] print("收到信息: ", msg_id, msg_from_user, msg_content, msg_create_time,msg_type)# 群聊信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True)def information(msg): msg_id = msg['MsgId'] msg_from_user = msg['ActualNickName'] msg_content = msg['Content'] msg_create_time = msg['CreateTime'] msg_type = msg['Type'] print("群聊信息: ",msg_id, msg_from_user, msg_content, msg_create_time,msg_type)if __name__ == '__main__': itchat.auto_login() itchat.run()
代码执行结果如下:
群聊信息: 2254622820807367335 胡小韬 对手公司 1533023277 Text群聊信息: 1765614482944449471 xia_ang 还有自干五 1533023285 Text好友信息: 615083621872361432 CoderPig 哈哈 1533023293 Text好友信息: 7292909308782687092 CoderPig 你好哦 1533023302 Text
嗯,信息提取成功,接下来要验证的是不同的信息类型和对应的处理方式,文字,图片(表情),音频,视频,文件五种,后面四种都需要下载到本地,itchat中提供了一个下载文件的方法msg['Text'](文件存储路径)
,调用这个方法即可完成文件下载,修改后的代码如下:
import itchatfrom itchat.content import *import osimport time# 文件临时存储页rec_tmp_dir = os.path.join(os.getcwd(), 'tmp/')# 存储数据的字典rec_msg_dict = {}# 好友信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isFriendChat=True)def handle_friend_msg(msg): msg_id = msg['MsgId'] msg_from_user = msg['User']['NickName'] msg_content = '' # 收到信息的时间 msg_time_rec = time.strftime("%Y-%m-%d %H:%M%S", time.localtime()) msg_create_time = msg['CreateTime'] msg_type = msg['Type'] if msg['Type'] == 'Text': msg_content = msg['Content'] elif msg['Type'] == 'Picture' or msg['Type'] == 'Recording' or msg['Type'] == 'Video' or msg['Type'] == 'Attachment': msg_content = r"" + msg['FileName'] msg['Text'](rec_tmp_dir + msg['FileName']) rec_msg_dict.update({ msg_id: { 'msg_from_user': msg_from_user, 'msg_time_rec': msg_time_rec, 'msg_create_time': msg_create_time, 'msg_type': msg_type, 'msg_content': msg_content } }) print(msg)# 群聊信息监听@itchat.msg_register([TEXT, PICTURE, RECORDING, ATTACHMENT, VIDEO], isGroupChat=True)def information(msg): msg_id = msg['MsgId'] msg_from_user = msg['ActualNickName'] msg_content = '' # 收到信息的时间 msg_time_rec = time.strftime("%Y-%m-%d %H:%M%S", time.localtime()) msg_create_time = msg['CreateTime'] msg_type = msg['Type'] if msg['Type'] == 'Text': msg_content = msg['Content'] elif msg['Type'] == 'Picture' or msg['Type'] == 'Recording' or msg['Type'] == 'Video' or msg['Type'] == 'Attachment': msg_content = r"" + msg['FileName'] msg['Text'](rec_tmp_dir + msg['FileName']) rec_msg_dict.update({ msg_id: { 'msg_from_user': msg_from_user, 'msg_time_rec': msg_time_rec, 'msg_create_time': msg_create_time, 'msg_type': msg_type, 'msg_content': msg_content } }) print(msg)if __name__ == '__main__': if not os.path.exists(rec_tmp_dir): os.mkdir(rec_tmp_dir) itchat.auto_login() itchat.run()
代码执行后,分别测试下发送各种文件,看是否都缓存下来,如图所示
接着到撤回信息的监控,撤回的信息类型是NOTE,我们监听下看看,看看撤回的信息具体内容。
{'MsgId': '7399110162640182490', 'FromUserName': '@9c1a8bf4e28771a6b3ab635991dea2a1', 'ToUserName': '@49d1d90b90371099297a08da1009f3cdd042f21194239ef47b60e8f0b52e4553', 'MsgType': 10002, 'Content': '<sysmsg type="revokemsg"><revokemsg><session>zpj779878443</session><oldmsgid>1625723544</oldmsgid><msgid>3154925139554625499</msgid><replacemsg><![CDATA["CoderPig" 撤回了一条消息]]></replacemsg></revokemsg></sysmsg>', 'Status': 4, 'ImgStatus': 1, 'CreateTime': 1533103679, 'VoiceLength': 0, 'PlayLength': 0, 'FileName': '', 'FileSize': '', 'MediaId': '', 'Url': '', 'AppMsgType': 0, 'StatusNotifyCode': 0, 'StatusNotifyUserName': '', 'RecommendInfo': {'UserName': '', 'NickName': '', 'QQNum': 0, 'Province': '', 'City': '', 'Content': '', 'Signature': '', 'Alias': '', 'Scene': 0, 'VerifyFlag': 0, 'AttrStatus': 0, 'Sex': 0, 'Ticket': '', 'OpCode': 0}, 'ForwardFlag': 0, 'AppInfo': {'AppID': '', 'Type': 0}, 'HasProductId': 0, 'Ticket': '', 'ImgHeight': 0, 'ImgWidth': 0, 'SubMsgType': 0, 'NewMsgId': 7399110162640182490, 'OriContent': '', 'EncryFileName': '', 'User': <User: {'MemberList': <ContactList: []>, 'Uin': 0, 'UserName': '@9c1a8bf4e28771a6b3ab635991dea2a1', 'NickName': 'CoderPig', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=625711027&username=@9c1a8bf4e28771a6b3ab635991dea2a1&skey=@crypt_2d4a1972_e0963a9b961045c2e06293043f1c98a8', 'ContactFlag': 3, 'MemberCount': 0, 'RemarkName': '', 'HideInputBarFlag': 0, 'Sex': 1, 'Signature': '不服气,就用行动去证明,少说多做...2018.7.29', 'VerifyFlag': 0, 'OwnerUin': 0, 'PYInitial': 'CODERPIG', 'PYQuanPin': 'CoderPig', 'RemarkPYInitial': '', 'RemarkPYQuanPin': '', 'StarFriend': 0, 'AppAccountFlag': 0, 'Statues': 0, 'AttrStatus': 33783847, 'Province': '广东', 'City': '江门', 'Alias': '', 'SnsFlag': 17, 'UniFriend': 0, 'DisplayName': '', 'ChatRoomId': 0, 'KeyWord': 'zpj', 'EncryChatRoomId': '', 'IsOwner': 0}>, 'Type': 'Note', 'Text': '"CoderPig" 撤回了一条消息'}
首先是判断系统信息是默认撤回了一条信息,内容是:
显示判断是否是撤回的信息,从上面可以看到这样的串字符串:<![CDATA["CoderPig" 撤回了一条消息]]>
,我们只需要写个正则判断是否有这样的内容,有说明是撤回的信息提醒,接着找撤回的信息MsgId,在上面这串信息的前面就是了:<msgid>3154925139554625499</msgid>
,同样用正则提取。接着要做的就是拿着MsgId去查存消息的字典,根据信息类型拼接文字或文字加文件,然后发送给文件传输助手。具体代码如下:
@itchat.msg_register([NOTE], isFriendChat=True, isGroupChat=True)def revoke_msg(msg): if revoke_msg_compile.search(msg['Content']) is not None: old_msg_id = extract_msgid_compile.search(msg['Content']).group(1) old_msg = rec_msg_dict.get(old_msg_id, {}) # 先发送一条文字信息 itchat.send_msg(str(old_msg.get('msg_from_user') + "撤回了一条信息:" + old_msg.get('msg_content')), toUserName="filehelper") # 判断文msg_content是否存在,不存在说明可能是 if os.path.exists(os.path.join(rec_tmp_dir, old_msg.get('msg_content'))): if old_msg.get('msg_type') == 'Picture': itchat.send_image(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper") elif old_msg.get('msg_type') == 'Video': itchat.send_video(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper") elif old_msg.get('msg_type') == 'Attachment' or old_msg.get('msg_type') == 'Recording': itchat.send_file(os.path.join(rec_tmp_dir, old_msg.get('msg_content')), toUserName="filehelper")
运行后测试下发送信息后撤回,看下是否生效,测试结果如图所示。
另外,我们的信息都是用一个字典存着的,时间长了的话,数据会很多,而且图片文件这些会越攒越多,我们可以添加一个定时任务,比如每隔五分钟清理一下创建时长超过2分钟的信息和对应的文件。相关代码如下:
# 每隔五种分钟执行一次清理任务def clear_cache(): # 当前时间 cur_time = time.time() # 遍历字典,如果有创建时间超过2分钟(120s)的记录,删除,非文本的话,连文件也删除 for key in list(rec_msg_dict.keys()): if int(cur_time) - int(rec_msg_dict.get(key).get('msg_create_time')) > 120: if not rec_msg_dict.get(key).get('msg_type') == 'Text': file_path = os.path.join(rec_tmp_dir, rec_msg_dict.get(key).get('msg_content')) print(file_path) if os.path.exists(file_path): os.remove(file_path) rec_msg_dict.pop(key)# 开始轮询任务def start_schedule(): sched.add_job(clear_cache, 'interval', minutes=2) sched.start()# 退出停止所有任务并清空缓存文件夹def after_logout(): sched.shutdown() shutil.rmtree(rec_tmp_dir)if __name__ == '__main__': sched = BlockingScheduler() if not os.path.exists(rec_tmp_dir): os.mkdir(rec_tmp_dir) itchat.auto_login(exitCallback=after_logout) itchat.run(blockThread=False) start_schedule()
本章我们对微信网页接口开源库itchat库进行了学习,通过三个实用的代码示例,相信你对这个库已经有个大概的了解了,你也可以根据自己的需求来定制一个自己的机器人,比如添加自动回复,特定信息监控转发等。另外,如果你想让自己的机器人一直运行,可以把脚本部署到服务器上运行,具体怎么运行可以参见爬虫部分的章节。
联系客服