Python实现微信公众号关键词自动回复功能详解
本文介绍如何利用Python实现微信公众号的关键词自动回复功能。通过解析用户发送的XML消息,提取ToUserName、FromUserName等关键字段,并结合web框架处理GET验证与POST数据接收。文章详细讲解了使用xml.etree.ElementTree解析XML,以及通过JSON文件接口获取数据并返回给用户的具体实现步骤,涵盖文本、图片、位置等多种消息类型的处理逻辑。
近期在参与国赛项目时,需要开发一个微信公众号应用。核心功能是根据用户发送的关键词调用数据接口,并将结果自动回复给用户。
为此,查阅了微信公众平台的相关开发文档,具体地址见文档链接。
依据入门指南,我理清了用户向公众号发送消息的交互机制。当用户发送消息时,微信公众平台会在开发者后台推送一个XML格式的数据包,因此需要编写Python脚本来解析该XML并实现自动回复功能。
假设用户向公众号发送了一段文本消息“hello”,后台接收到的XML数据结构如下所示。
<xml> <ToUserName><![CDATA[公众号]]></ToUserName> <FromUserName><![CDATA[粉丝号]]></FromUserName> <CreateTime>1460541339</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[hello]]></Content> </xml>
请注意,XML中包含几个对开发者至关重要的字段:ToUserName(接收方账号)、FromUserName(发送方账号)、MsgType(消息类型)以及Content(消息内容)。掌握这些字段的信息,即可实现自动回复逻辑。
在上述示例中,MsgType的值为“text”。微信支持的MsgType类型包括:“text”(文本)、“image”(图片)、“voice”(语音)、“video”(视频)、“shortvideo”(短视频)、“location”(位置)、“link”(链接)以及“event”(事件)。
首先,创建main.py文件作为入口。
main.py的代码结构如下。
# -*- coding: utf-8 -*- # filename: main.py import web from handle import Handle urls = ( '/wx', 'Handle', ) if __name__ == '__main__': app = web.application(urls, globals()) app.run()
接着编写receive.py脚本,用于接收用户发送的数据,解析XML,并返回相应的数据对象。
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':
#print('text')
return TextMsg(xmlData)
elif msg_type == 'image':
return ImageMsg(xmlData)
elif msg_type == 'location':
#print('location')
return LocationMsg(xmlData)
elif msg_type == 'event':
#print('event')
return EventMsg(xmlData)
class Event(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.Eventkey = xmlData.find('EventKey').text
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
class LocationMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Location_X = xmlData.find('Location_X').text
self.Location_Y = xmlData.find('Location_Y').text
class EventMsg(Msg):
def __init__(self, xmlData):
Event.__init__(self, xmlData)
self.Event = xmlData.find('Event').text
在此脚本中,使用了xml.etree.ElementTree库,这是一个简洁高效的XML解析与创建API。通过fromstring()函数解析XML字符串,并利用find()方法根据标签名获取指定内容。
此外,还需编写reply.py脚本用于生成并发送回复消息。前文提到,用户向公众号发送消息时,后台会接收到XML;同理,公众号向用户发送消息时,也是发送一个XML数据包,区别仅在于ToUserName和FromUserName的值互换,内容则由开发者自定义。
<xml> <ToUserName><![CDATA[粉丝号]]></ToUserName> <FromUserName><![CDATA[公众号]]></FromUserName> <CreateTime>1460541339</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[test]]></Content> </xml>
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.py的具体代码如下。
import web
import reply
import receive
import JsonData
import xml.dom.minidom
class Handle(object):
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "hello, this is handle view"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = "hello2016"
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
map(sha1.update, list)
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):
try:
webData = web.data()
#print(webData)
recMsg = receive.parse_xml(webData)
#print(recMsg)
if isinstance(recMsg, receive.Msg):
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
if recMsg.MsgType == 'text':
try:
a = JsonData.praserJsonFile()
#print(a)
except Exception as Argument:
return Argument
if a['status'] == '1':
content = "No equipment"
else:
if a['data'][0]['weather']=='0':
israin = '7.没有下雨'
else:
israin = '7.下雨'
#print(israin)
content = "此设备数据如下:\n"+"1.id号为 "+a['data'][0]['id']+"\n"+"2.温度为 "+a['data'][0]['temp']+"\n"+"3.湿度为 "+a['data'][0]['humidity']+"\n"+"4.PM2.5浓度为 "+a['data'][0]['pm25']+"ug\n"+"5.PM10浓度为 "+a['data'][0]['pm10']+"\n"+"6.光照 "+a['data'][0]['illumination']+"L\n"+israin
#content = "%s\n%s %s\n%s %s\n%s %s\n%s %s\n%s %s\n%s" %('环境数据如下:','设备id号为',a['data']['id'],'temp is', a['data']['temp'], 'humidity is', a['data']['humidity'],'PM25 is',a['data']['pm25'],'illumination',a['data']['illumination'],israin)
#print(content)
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
if recMsg.MsgType == 'image':
mediaId = recMsg.MediaId
replyMsg = reply.ImageMsg(toUser, fromUser, mediaId)
return replyMsg.send()
if recMsg.MsgType == 'location':
location_x = recMsg.Location_X
location_y = recMsg.Location_Y
content = "您所在的位置是在:经度为"+location_x+";纬度为:"+location_y
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
if recMsg.MsgType == 'event':
#print('yes')
event = recMsg.Event
if event == 'subscribe':
content = "欢迎关注,您好!雨燕城市环境小助手微信公众号:发送 获取数据,公众号会自动发送当前环境数据(目前为调试数据,不是真实数据).将要调试GPS,根据手机定位位置与设备位置相关联,取最近距离的设备所获取到的数据并进行返回."
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
else:
return reply.Msg().send()
else:
print("not do")
return reply.Msg().send()
except Exception as Argment:
return Argment
注:上述代码展示了目前实现的所有功能,包括接收关键字并自动返回数据、关注后自动发送欢迎语,以及发送位置信息以获取GPS数据。
那么,如何调用服务器上的数据呢?由于已有数据的JSON文件,可以使用Python脚本解析JSON,并将解析后的数据填充到回复内容的content字段中即可。
以下是JSON文件解析的相关代码。
import types import urllib.request import json def praserJsonFile(): url = "http://118.89.244.53:8080/index.php/home/api/present_data" data = urllib.request.urlopen(url).read() value = json.loads(data.decode()) #print(value) #print(value['data']) return value #praserJsonFile()
这里的value变量即为从JSON解析后得到的列表数据。
以上就是本文的全部内容,希望对各位的学习有所帮助,也欢迎大家继续支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









