最近用python写了一个聊天机器人的微信公众号,网上找的开发文档参差不齐,官方文档也比较老旧,还有部分小问题。于是,分享一下我的思路。

开发环境

windows sever 2008+python3.6+Flask

1.搭建服务器

服务器:随便租个什么服务器,或者用弄个能连外网的机子。
1.1 在服务器上装python3.6开发环境,个人喜欢装Anaconda,省得手动装各种包。
1.2 新建一个test.py文件,写如下代码:

from flask import Flask, request, make_responseapp = Flask(__name__)
app.debug = True@app.route('/')  # 默认网址
def index():return '测试页面'if __name__ == '__main__':app.run(host="0.0.0.0", port=80,debug=True)

打开终端,cd到目录下,运行这个文件
python test.py
弹出 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit) 说明成功了。
注意这里的端口必须是80或443,微信公众号只支持这两个端口。
1.3 在另一台机子上打开浏览器,输入服务器的ip地址,弹出‘测试页面’表示服务器搭建成功。
注意:使用阿里云服务器显示连接超时,需要配置安全组规则,具体方法百度。

2. 配置微信公众号

2.1 首先当然是申请一个公众号啦。注意:素材和菜单的开发权限必须要通过认证,未认证只能发发文字,具体可以去接口权限查看。
2.2 开发者基本配置

点击修改配置后,选择提交肯定是验证token失败,因为还需要完成代码逻辑。
2.3 改动 test.py 为app.py.代码如下:

# -*- coding: utf-8 -*-
#  filename: app.py
from flask import Flask, request, make_response
import hashlib
import handleapp = Flask(__name__)
app.debug = True@app.route('/')  # 默认网址
def index():return '测试页面'@app.route('/wx', methods=['GET', 'POST'])
def wechat_auth():  # 处理微信请求的处理函数,get方法用于认证,post方法取得微信转发的数据if request.method == 'GET':token = 'xxxx'  # 这里填公众号里设置的token。data = request.argssignature = data.get('signature', '')timestamp = data.get('timestamp', '')# print(timestamp)nonce = data.get('nonce', '')echostr = data.get('echostr', '')s = [timestamp, nonce, token]s.sort()s = ''.join(s)s = s.encode(encoding='utf-8')#加密后的token匹配上signatureif (hashlib.sha1(s).hexdigest() == signature):return make_response(echostr)else:return 'signature is error'else:rec = request.stream.read() # 接收消息#print(rec)dispatcher = handle.MsgHandler(rec)data = dispatcher.dispatch()with open("./debug.log", "a") as file:file.write(data)response = make_response(data)response.content_type = 'application/xml'return response #发送消息if __name__ == '__main__':app.run(host="0.0.0.0", port=80,debug=True)

这里只用到wechat_auth函数中的get请求部分,else后面是post请求,暂且不管,import handle是用来处理消息的,先不要看。
运行app.py python app.py
2.4 运行app.py成功后,点击公众号界面的提交按钮,token验证成功即可。

3. 消息回复流程

核心文件

app.py : 服务器主程序,接收和发送消息。
handle.py:消息处理枢纽。
receive.py:解析接收到的消息
reply.py:回复消息。(可以添加复杂的回复功能)

流程图


有了以上几个模块就可以完成简单的文字消息回复了。

4.access_token

官方文档:access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
可以看到获取一次access_token的有效期是2小时,那么我们需要写一个定时程序,每2小时获取一次access_token。保存在文本文档access_token.txt中。
basic.py :定时获取access_token

# -*- coding: utf-8 -*-
#  filename: basic.py
import urllib.request
import time
import jsonclass Basic():def __init__(self):self.__accessToken = ''def __real_get_access_token(self):appId = "xxxxx"  #配置公众号时得到的id和secretappSecret = "xxxxxx"postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type="               "client_credential&appid=%s&secret=%s" % (appId, appSecret))urlResp = urllib.request.urlopen(postUrl)urlResp = json.loads(urlResp.read())self.__accessToken = urlResp['access_token']def get_access_token(self):self.__real_get_access_token()return self.__accessTokenif __name__ == "__main__":basic = Basic()while True:token = basic.get_access_token()with open('access_token.txt','w',encoding='utf-8') as f:f.write(token)time.sleep(7000)

运行basic.py就能持续不断的获取access_token啦

5.临时素材添加与获取

如果你向公众号发送语音、图片、视频等非文本消息,app.py只会接收到一个这样的消息(消息模板),例如图片消息:

<xml><ToUserName><![CDATA[接收人]]></ToUserName><FromUserName><![CDATA[发送人]]></FromUserName><CreateTime>创建时间</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[素材id]]></MediaId></Image></xml>

这样一段消息经过receive.py的解析后,我们拿到“素材id”,然后向微信服务器发送一个请求,才能下载到对应的素材。
同理,如果公众号向用户发送图片也需要先上传图片到微信服务器,服务器会返回一个“素材id”,然后公众号再发送一段类似上面的消息,填入返回的”素材id“就可以了,其它类型消息模板可以去微信公众号官方文档查看。
media.py:素材上传和下载。

# -*- coding: utf-8 -*-
# filename: media.py
import requests
import json
import osclass Media(object):def __init__(self):with open('access_token.txt','r',encoding='utf-8') as f:   #获取access_tokentoken = f.read()self.accessToken = tokendef get(self, mediaId, filepath):postUrl = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s" % (self.accessToken, mediaId)urlResp = requests.post(postUrl)headers = urlResp.headersif ('Content-Type: application/json\r\n' in headers) or ('Content-Type: text/plain\r\n' in headers):jsonDict = json.loads(urlResp.content)print(jsonDict)else:# print(urlResp.content.decode('ascii'))buffer = urlResp.content   #素材的二进制with open(filepath, "wb") as f:f.write(buffer)print("get successful")def _add(self, type,filepath):with open(filepath,"rb") as f:img = f.read()filename = os.path.split(filepath)[-1]url = 'https://api.weixin.qq.com/cgi-bin/media/upload?access_token={}&type={}'files = {'media':(filename,img)}res = requests.post(url.format(self.accessToken,type),files=files)return json.loads(res.content.decode('utf-8'))['media_id']

5.自定义菜单

自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示:

menu.py :自定义菜单增删改查

# -*- coding: utf-8 -*-
# filename: menu.py
import urllib.requestclass Menu(object):def __init__(self):passdef create(self, postData, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s" % accessToken# if isinstance(postData, 'unicode'):postData = postData.encode('utf-8')urlResp = urllib.request.urlopen(url=postUrl, data=postData)print(urlResp.read())def query(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s" % accessTokenurlResp = urllib.request.urlopen(url=postUrl)print(urlResp.read())def delete(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s" % accessTokenurlResp = urllib.request.urlopen(url=postUrl)print(urlResp.read())#获取自定义菜单配置接口def get_current_selfmenu_info(self, accessToken):postUrl = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=%s" % accessTokenurlResp = urllib.request.urlopen(url=postUrl)print(urlResp.read())if __name__ == '__main__':myMenu = Menu()postJson = """{"button":[{"name": "小迪互动","sub_button":[{"type": "click","name": "图片文字识别","key": "ocr"},{"type": "click","name": "语音聊天","key": "speech"},{"type": "click","name": "文字聊天","key": "text"}]},{"name": "精选栏目","sub_button":[{"type": "view","name": "往期文章","url": "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzU4ODcxOTE2OA==&scene=124#wechat_redirect"}]},{"name": "关于我们","sub_button":[{"type": "click","name": "DepthsData","key": "depth"},{"type": "click","name": "RPA","key": "rpa"}]}]}"""with open('access_token.txt','r',encoding='utf-8') as f:accessToken = f.read()myMenu.create(postJson, accessToken)

运行menu.py一次性创建自定义菜单,有些许延迟。
创建成功后,点击菜单会向公众号发送一个“事件消息”,根据你自定义的click-key,判断该事件属于哪个菜单。
此外,新用户的订阅会发送一个“subscribe”的“事件消息”,可以利用它给新用户打招呼噢。

6. 总结

到此,一个功能还算齐全的公众号开发完毕。
1.补充完整的流程图:

图中红色字体的文件是需要运行的。
2.文件结构:

3.剩余代码
handle.py

# -*- coding: utf-8 -*-
# filename: handle.py
from receive import parse_xml
from reply import event_reply,text_reply,ocr_reply
from media import Media
from helpfunc import speech2text,text2speechclass MsgHandler(object):"""针对type不同,转交给不同的处理函数。直接处理即可"""def __init__(self, msg):self.msg = parse_xml(msg)self.time = int(time.time())self.temp_text = """<xml><ToUserName><![CDATA[{}]]></ToUserName><FromUserName><![CDATA[{}]]></FromUserName><CreateTime>{}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[{}]]></Content></xml>"""self.temp_image = """<xml><ToUserName><![CDATA[{}]]></ToUserName><FromUserName><![CDATA[{}]]></FromUserName><CreateTime>{}</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[{}]]></MediaId></Image></xml>"""self.temp_voice = """<xml><ToUserName><![CDATA[{}]]></ToUserName><FromUserName><![CDATA[{}]]></FromUserName><CreateTime>{}</CreateTime><MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[{}]]></MediaId></Voice></xml>"""def dispatch(self):self.result = ""  # 统一的公众号出口数据if self.msg.MsgType == "text":self.result = self.textHandle()elif self.msg.MsgType== "voice":self.result = self.voiceHandle()elif self.msg.MsgType == 'image':self.result = self.imageHandle()elif self.msg.MsgType == 'video':self.result = self.videoHandle()elif self.msg.MsgType == 'shortvideo':self.result = self.shortVideoHandle()elif self.msg.MsgType == 'location':self.result = self.locationHandle()elif self.msg.MsgType == 'link':self.result = self.linkHandle()elif self.msg.MsgType == 'event':self.result = self.eventHandle()return self.resultdef textHandle(self):result = text_reply(self.msg.Content,self.msg.FromUserName)response = self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, result)return responsedef voiceHandle(self):# template = self.temp_voicemediaid = self.msg.MediaIdfilepath = r'voice/' + str(time.time()) + '.mp3'Media().get(mediaid, filepath)#下载素材text = speech2text(filepath)result = text_reply(text,self.msg.FromUserName)voice_path = text2speech(result)r_mediaid = Media()._add('voice', voice_path)#上传素材response = self.temp_voice.format(self.msg.FromUserName, self.msg.ToUserName, self.time, r_mediaid)return responsedef imageHandle(self):mediaid = self.msg.MediaIdstyle = get_state(self.msg.FromUserName)if style == 'img-ocr':filepath = r'pic/' + str(time.time()) + '.jpg'Media().get(mediaid, filepath)try:result = ocr_reply(imgpath=filepath)except:result = '图上好像没有字吧'respnse = self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, result)else:respnse = self.temp_image.format(self.msg.FromUserName, self.msg.ToUserName, self.time, mediaid)return respnsedef videoHandle(self):return self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, '不支持的消息类型!')def shortVideoHandle(self):return self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, '不支持的消息类型!')def locationHandle(self):return self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, '不支持的消息类型!')def linkHandle(self):return self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, '不支持的消息类型!')def eventHandle(self):result = event_reply(self.msg,self.msg.FromUserName)response = self.temp_text.format(self.msg.FromUserName, self.msg.ToUserName, self.time, result)return response

receive.py

# -*- coding: utf-8 -*-
# filename: receive.py
import xml.etree.ElementTree as ETdef parse_xml(web_data):if len(web_data) == 0:return NonexmlData = ET.fromstring(web_data)msg_type = xmlData.find('MsgType').textprint(msg_type)if msg_type == 'event':event_type = xmlData.find('Event').textif event_type == 'CLICK':return Click(xmlData)elif event_type in ('subscribe', 'unsubscribe'):return Subscribe(xmlData)#elif event_type == 'VIEW':#return View(xmlData)#elif event_type == 'LOCATION':#return LocationEvent(xmlData)#elif event_type == 'SCAN':#return Scan(xmlData)elif msg_type == 'text':return TextMsg(xmlData)elif msg_type == 'image':return ImageMsg(xmlData)elif msg_type == 'voice':return VoiceMsg(xmlData)else:return Msg(xmlData)class Msg(object):def __init__(self, xmlData):self.ToUserName = xmlData.find('ToUserName').textself.FromUserName = xmlData.find('FromUserName').textself.CreateTime = xmlData.find('CreateTime').textself.MsgType = xmlData.find('MsgType').textclass Click(Msg):def __init__(self, xmlData):Msg.__init__(self, xmlData)self.Event = xmlData.find('Event').textself.Eventkey = xmlData.find('EventKey').textclass Subscribe(Msg):def __init__(self, xmlData):Msg.__init__(self, xmlData)self.Event = xmlData.find('Event').textclass TextMsg(Msg):def __init__(self, xmlData):Msg.__init__(self, xmlData)self.Content = xmlData.find('Content').textclass ImageMsg(Msg):def __init__(self, xmlData):Msg.__init__(self, xmlData)self.PicUrl = xmlData.find("PicUrl").textself.MediaId = xmlData.find("MediaId").textclass VoiceMsg(Msg):def __init__(self, xmlData):Msg.__init__(self, xmlData)self.MediaId = xmlData.find("MediaId").text

reply.py

放心吧,这个我是不会放出来的,毕竟我是一个“称职”的员工。

好了,以上就是用python开发微信公众平台的全部了,觉得好看点个赞再走吧!

用python开发微信公众平台聊天机器人相关推荐

  1. python微信爬取教程_PYTHON爬虫之旅系列教程之【利用Python开发微信公众平台一】...

    感谢大家的等待,好啦,都准备好瓜子.板凳,老司机要发车啦-- 本系列课程讲述"PYTHON爬虫之旅",具体大纲可参考:[PYTHON爬虫之旅]概要目录. 本节课讲述如何利用Pyth ...

  2. python 开发微信公众平台 的坑

    1.requests.post获取数据失败 python post内容json.jump() 才可以,不然是字典形式: 2.python3代码到python2.7出现编码问题 python2.7编码问 ...

  3. Python开发微信公众平台(一)

    文章为个人开发经历的记录 1.在系统为Ubuntu的Linux下创建一个虚拟机实例,并绑定浮动IP 2.用xshell5连接虚拟机,开始配置. a.配置网络 sudo vim /etc/profile ...

  4. 【毕业设计_课程设计】基于python的微信公众平台机器人的设计与实现

    文章目录 0 项目说明 项目介绍 项目工程 0 项目说明 基于python的微信公众平台机器人的设计与实现 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 项目介绍 1.NGINX做负载均衡, ...

  5. 毕业设计 python的微信公众平台机器人

    文章目录 0 项目说明 项目介绍 项目工程 0 项目说明 基于python的微信公众平台机器人的设计与实现 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 项目介绍 1.NGINX做负载均衡, ...

  6. python开发微信订阅号如何申请_基于Python的微信公众平台二次开发(Python常用框架、订阅号开发、公众号开发)...

    1.1.课程的背景 微信公众平台的火热程度已经不用多言,无论是个人还是企业,政府还是商家,都已经开始搭建微信公众平台,微信的作用已经被各界人士认可.微信公众平台的技术需求市场缺口巨大.同时python ...

  7. 公众平台 python_轻松实现python搭建微信公众平台

    本文主要是一步一步教大家如何利用python搭建微信公众平台,有兴趣的朋友可以参考一下 使用的工具,python 新浪SAE平台,微信的公众平台 你需要先在微信的公众平台与新浪SAE平台上各种注册,微 ...

  8. python微信公众号框架_轻松实现python搭建微信公众平台

    本文主要是一步一步教大家如何利用python搭建微信公众平台,有兴趣的朋友可以参考一下 使用的工具,python 新浪SAE平台,微信的公众平台 你需要先在微信的公众平台与新浪SAE平台上各种注册,微 ...

  9. .net开发微信公众平台

    (转自:fhx900808.blog.163.com/blog/static/2099660212013423113226312/) 一.说明:公众平台信息接口为开发者提供了一种新的消息处理方式,只有 ...

最新文章

  1. sqlplus 登录数据库
  2. kaggle (02) - 房价预测案例(进阶版)
  3. Python修饰符--函数修饰符 “@”
  4. 20210422:力扣第237周周赛题解记录(上)
  5. AppWidget实现机制分析--什么是桌面插件
  6. 每日一题_36. 有效的数独
  7. angular 点菜_Vue2与Angular5实现无人点餐、无人收银系统项目实战视频教程【组合套餐】(大地)...
  8. 电商后台之【商品管理系统】
  9. 计算机切换用户界面,win7系统登录界面切换用户的方法
  10. 计算机网络怎么换ip,怎么更改电脑上网的IP地址
  11. 磨金石教育摄影干货分享|风光摄影后期教程:冷色调变暖色调
  12. 基于Patachmatch的stereo matching笔记(三):《PatchmatchNet》
  13. java毕业设计摄影服务管理系统服务端mybatis+源码+调试部署+系统+数据库+lw
  14. 网页设计志愿招募平台模块
  15. Arcgis 地理坐标系转投影坐标系(WGS84转CGCS2000)
  16. Xshell如何连接虚拟机
  17. 如何用中国知网导出参考文献
  18. 如何PHP给人生日祝福,送给网友的生日祝福语 朋友的祝福语
  19. 考试自动评分系统C语言改错,基于XML结构的C语言考试的自动评分系统.doc
  20. 抖音xgorgon和设备注册算法

热门文章

  1. System.InvalidOperationException:Each parameter in the deserialization constructor on type ‘‘ must
  2. 输入日期判断这一年的第几天
  3. 徐佐君:智慧园区网络 开放使能共赢
  4. ucos 学习:STM32F107 学习板 资料收集
  5. 微信公众号注册操作流程
  6. 计算机l符号代表什么意思,衣服sml代表什么意思 分别是什么的标记
  7. POJ 2240 HDU 1217 Arbitrage(Floyd)
  8. STM32项目总结--物联网毕设使用
  9. 商品订单管理系统java_Javaweb的实例--订单管理系统--设计数据库
  10. 基于词表和N-gram算法的新词识别实验