作者:JiawuZhang
出品:JiawuLab(ID:jiawulab)
实验记录系列是JiawuLab原创栏目,通过真实项目的操作,记录整个实验过程。
旨在通过一步步过程,无基础的朋友都能直接上手。
大家好,我是JiawuZhang,本次实验记录的项目是--微信公众号开发。
选择这个项目的原因有二:
1、微信公众号还是大部分人(包括我自己)获取信息的一个重要渠道;
2、正好我手上有个公众号;
好了,闲话不多说,直接进入主题。
效果演示
先放出实验过程中效果图,闪耀登场:
这么有个性的回复是不是很好玩,不再是固定的几个回复,具体的实现方法请往下看。
您也可以关注公众号:JiawuLab,来体验更多功能。
前期准备工作
一、准备一个微信公众号,我的是个人订阅号,未认证。(ps:个人公众号,不能认证)
认不认证都不要紧,只是接口权限多少的问题,未认证的接口已经能满足这次实验目的。
(具体权限可见-公众号后台-开发-接口权限)
二、准备一台服务器,现在提供云服务器的厂家很多,大家可自行准备。
建议选择独立ip的服务器,(共享ip会便宜些,但是不推荐,具体原因后面会说到。)
我选择的是某厂的1核2G1M(1核cpu,2G内存,1M带宽)的独立ip服务器,具体品牌就不说了,怕有广告嫌疑。
这个配置应付常规公众号足够了,但是如果你的公众号的流量特别大,需要根据自己的情况增加配置。
三、服务器上安装python环境,我的环境是python3.7.4, Centos7.5。
如果你不清楚怎么安装,可关注公众号:JiawuLab,给我留言,这里不展开。
开发方向
微信公众号开发,官方的技术文档,写的很详细,也有范例参照。
另外,github上获得3000多星的WeRoBot,也是一个很受欢迎的微信公众号开发框架,有兴趣的朋友可自行了解。
上面提到的相关资源链接,请到文末获取。
我们这次以官方开发文档来进行实验。
实验开始
首先,进入官方开发文档-入门指引,如下图:
我们只用关注红框中的内容,这里是python2.7版本,服务器上是python3.7.4版本。
所以后面需要对该指引所提到的内容进行版本转化,我会在后面提到如何转,不用担心。
然后需要安装web.py,这是一个python的轻型web开发框架,我们只来用,不用特别学习,如需自行百度。
现在,在服务器中进行安装web.py,很简单,直接用pip安装就好
# 安装web.py
pip(3) install web.py
这里需要注意两点:
1、centos自带python2.7,如果安装python3版本没有改软链接,需要用pip3,检查方式,直接输入python -V
,看版本
2、web.py版本,pip安装的直接是0.40正式版,支持python3,不用像网上说的那种指定版本
安装完好,我们测试一下,拷贝下面的代码,保存为main.py文件
# -*- coding: utf-8 -*-
# filename: main.py
import web
urls = (
'/wx', 'Handle',
)
class Handle(object):
def GET(self):
return "pika pika pikaqiu!"
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
然后找到当前文件所在目录输入python(3) main.py 80
,服务器启动成功,本地打开网页,输入http://外网ip:80/wx
,显示如下
说明配置没有问题,这里记得一定要服务器将80端口放行。到这里,我们就可以正式进行公众号的配置及操作了。
微信公众号配置
打开微信公众号后台,找到-开发-基本配置-服务器配置,进行如下配置:
ps:这里先按照明文模式进行,后面会改成安全模式,过程都会涉及的。点击提交,出现报错,说Token验证失败,果然和文档中说的一样,出现错误,先不管它。
继续看文档后面的内容,原来是服务器没有进行配置,所以报错,我们按照文档后面的来。
先将之前的main.py文件改成如下:(注意备注)
# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle # 将Handle类放进handle.py文件中,再导入进来
urls = (
'/wx', 'Handle',
)
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
然后新建handle.py文件,里面写入:(注意备注)
# -*- coding: utf-8 -*-
# filename: handle.py
import hashlib
import web
class Handle(object):
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "pika pika pikaqiu!"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = "xxxx" #请按照公众平台官网\基本配置中信息填写
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
# map函数在python2和3的功能不同,需要转化
# map(sha1.update, list)
sha1.update(list[0].encode('utf-8'))
sha1.update(list[1].encode('utf-8'))
sha1.update(list[2].encode('utf-8'))
hashcode = sha1.hexdigest()
# 这里print转为python3
print("handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return ""
except Exception as Argument: # 这里加as 转为python3
return Argument
备注中已提到python3转化的位置, 然后在token中填入微信公众号中对应的token。
保存后输入python(3) main.py 80
,服务器启动。
再次回到微信公众号的配置,点击提交,配置成功,显示如下:
现在微信公众号就配置好了,打通服务器与微信公众号这一步很关键,所有后续功能都是在此基础上开发的。
回复功能实现
到这里,指引列出两个示例,分别是你说我学、图尚往来,展示文字回复和图片回复功能。
另外还展示了获取accessToken、获取临时素材、获取永久素材、自定义菜单等四个功能演示。
现在根据自己的需求,以及获得接口权限来决定选择哪些功能,比如我没有自定义菜单权限,就不用理它。
而图片回复需要用到临时素材或是永久素材功能,来调用不同图片,这些都可根据实际需求来选用。
我需要用到的是文字回复,所以这里实现文字回复演示。
官方文档中关于文字回复的机制,以及注意事项很详细,大家先仔细研究一下,比如消息体收到和回复的区别,消息回复流程等。
这里只做一个关键点的提醒,就是回复success问题:
1、假如服务器无法保证在五秒内处理回复,则必须回复“success”或者“”(空串),否则微信后台会发起三次重试。
2、三次重试后,依旧没有及时回复任何内容,系统自动在粉丝会话界面出现错误提示“该公众号暂时无法提供服务,请稍后再试”。
这是什么意思呢?是指需要保证你的功能代码,在服务器收到消息后,5秒内做出反应,这也是为什么我不推荐共享IP服务器的缘故。
在功能代码中,我们可能需要做一些递归运算,数据库的查询等花时间的事情,而共享IP服务器会受到其他网站的很大影响。
到时可能你写了很厉害的功能,因为无法在5秒内实现,最后给读者的展示,就是无尽的“该公众号暂时无法提供服务,请稍后再试”
好了,码代码的时间到了,这里直接将示例中的三段代码保存即可。
分别是接受消息代码、回复消息代码、handle类的改写,如下:
接受消息代码,保存为receive.py
# -*- coding: utf-8 -*-
# filename: receive.py
import xml.etree.ElementTree as ET
def parse_xml(web_data):
if len(web_data) == 0:
return None
xmlData = ET.fromstring(web_data)
msg_type = xmlData.find('MsgType').text
if msg_type == 'text':
return TextMsg(xmlData)
elif msg_type == 'image':
return ImageMsg(xmlData)
class Msg(object):
def __init__(self, xmlData):
self.ToUserName = xmlData.find('ToUserName').text
self.FromUserName = xmlData.find('FromUserName').text
self.CreateTime = xmlData.find('CreateTime').text
self.MsgType = xmlData.find('MsgType').text
self.MsgId = xmlData.find('MsgId').text
class TextMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Content = xmlData.find('Content').text.encode("utf-8")
class ImageMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.PicUrl = xmlData.find('PicUrl').text
self.MediaId = xmlData.find('MediaId').text
回复消息代码,保存为reply.py
# -*- coding: utf-8 -*-
# filename: reply.py
import time
class Msg(object):
def __init__(self):
pass
def send(self):
return "success"
class TextMsg(Msg):
def __init__(self, toUserName, fromUserName, content):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['Content'] = content
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{Content}]]></Content>
</xml>
"""
return XmlForm.format(**self.__dict)
class ImageMsg(Msg):
def __init__(self, toUserName, fromUserName, mediaId):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['MediaId'] = mediaId
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[{MediaId}]]></MediaId>
</Image>
</xml>
"""
return XmlForm.format(**self.__dict)
上面两个文件只用保存就可以,不过细心的你会发现,里面除了文字,还有图片功能,这个后面再说。
有了接受和回复文件后,我们就要在handle.py文件中对Handle类进行改写,如下:
# filename: handle.py
import hashlib
import reply # 导入回复文件
import receive # 导入接收文件
import web
class Handle(object):
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "pika pika pikaqu!"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = 'XXXX' # 请按照公众平台官网\基本配置中信息填写
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
sha1.update(list[0].encode('utf-8'))
sha1.update(list[1].encode('utf-8'))
sha1.update(list[2].encode('utf-8'))
hashcode = sha1.hexdigest()
print("handle/GET func: hashcode, signature: ", hashcode, signature)
if hashcode == signature:
return echostr
else:
return ""
except Exception as Argument:
return Argument
def POST(self): # 新增加的POST函数
try:
webData = web.data()
print("Handle Post webdata is ", webData)
# 后台打日志
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
content = "test" # 这里是回复内容
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
else:
print("暂且不处理")
return "success"
except Exception as Argment:
return Argment
现在代码都写好了,保存后输入python(3) main.py 80
,服务器启动。然后就可以进行测试了。
正好在测试期间有读者回复,下面是截图:
初步test成功,不过还没有达到我们的要求。现在简单的消息回复功能就实现了。
下一步预告
经过一番摸索,我们打通了服务器与微信公众号,也简单实现了消息回复功能,尽管还不是那么理想。
另外在测试中发现,原来设置的自定义菜单不见了,关注也没有回复了,回复语音达不到效果。。。
下一步,我们将进行如下实验,对接智能机器人,让回复更有个性;关注与关键字回复;语音也能很好的回复;自定义菜单又出现了等功能。
本期实验记录到此结束,感谢您的阅读。如果您喜欢这期文章,请点赞,支持一下。
您可以关注公众号:JiawuLab,提前体验更多功能,或者给我留言,说说你遇到的问题,我们一起探讨。
文中资源:
1、微信公众号官方文档地址:
https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
2、WeRoBot github地址:
https://github.com/offu/WeRoBot/tree/master/werobot
3、WeRoBot文档地址:
https://werobot.readthedocs.io/zh_CN/latest/
▼▼▼▼▼▼