利用微信公眾平台提供的消息接口,搭建自己的消息處理服務器,消息的處理和回復將更加靈活,以期給訂閱用戶提供更加定制化和個性化的信息。本文將結合SAE,基於Python Flask框架,搭建一個公眾賬號“豆米查書”(微信號doumibook)的消息服務器。該公眾號的基本功能是:輸入書籍標題、作者或者isbn條碼號等關鍵字,查詢書籍的基本信息。書籍數據來源豆瓣網,使用了douban api v2。
一、申請微信公眾平台賬號
到這里注冊微信公眾平台賬號,登陸,設置必要的名稱、地區和用戶信息等內容。在“高級功能”中開啟“開發模式”。
仔細閱讀微信公眾平台的官方文檔。
二、在SAE上創建Python應用
創建Python應用,二級域名和應用名稱為doumibook,那么基本地址為http://doumibook.sinaapp.com。
三、申請消息接口並驗證
1,消息接口配置
到“高級功能”-->“開發模式”-->“成為開發者”中填寫接口配置信息,有URL和Token,URL用於接口驗證和消息推送,本例中填寫步驟二中的接入地址,Token用於接口驗證,任意填寫即可,例如填寫“doumitest”。
2,消息接口驗證(網址接入)
信息填寫完成后,點擊提交,會提示認證“服務器未正確響應的Token驗證”,這是正常的,因為我們雖然創建了SAE應用,但沒有處理接口驗證消息。點擊提交時,微信服務器會發送一條Http GET請求,攜帶signature、timestamp、nouce和echostr四個參數,對URL進行合法性的校驗(參考微信官方的詳細文檔的“網址接入”小節)。如下是flask中處理接口驗證的代碼:
import hashlib @app.route('/weixin', methods=['GET']) def weixin_verify(): signature = request.args.get('signature') timestamp = request.args.get('timestamp') nonce = request.args.get('nonce') echostr = request.args.get('echostr') token = 'doumitest' #和申請消息接口時的Token一致 tmplist = [token, timestamp, nonce] tmplist.sort() tmpstr = ''.join(tmplist) hashstr = hashlib.sha1(tmpstr).hexdigest() if hashstr == signature: return echostr #success return 'access verification fail' #fail
四、消息推送和消息回復
1,消息推送
URL接口驗證以后,公眾平台賬號收到的消息將由微信服務器使用HTTP POST推送至該URL。消息內容為XML格式,消息類型有文本、圖片和地理位置等,例如文本消息由以下幾個部分組成。
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> </xml>
其中,ToUserName和FromUserName分別為公眾帳號和用戶帳號的表識串。MsgType標識消息類型,“text”表明消息是文本消息,文本內容放置在Content字段。其他類型消息的xml結構請參照官方文檔。
使用下面的代碼片段,解析xml數據,以dict形式保存:
import xml.etree.ElementTree as ET def parse_msg(rawmsgstr): root = ET.fromstring(rawmsgstr) msg = {} for child in root: msg[child.tag] = child.text return msg
2,用戶訂閱消息
當有新用戶關注公眾號時,微信服務器會發送一條通知消息到消息服務器,消息服務器可以返回初次訂閱的歡迎和幫助信息。目前,微信啟用了新的用戶訂閱通知方法,即使用事件推送。
事件推送的MsgType為“event”,是微信4.5版開始支持的一種消息格式,可以發送用戶訂閱( subscribe)、退訂(unsubscribe)以及自定義菜單點擊(CLICK)等事件消息。消息格式為:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[EVENT]]></Event> <EventKey><![CDATA[EVENTKEY]]></EventKey> </xml>
微信官方已經發布通告,在2013年3月26日起由原來的發送一條內容為“Hello2BizUser”的訂閱消息,切換至事件推送方法。
3,書籍信息獲取
簡單起見,目前僅支持微信用戶發送書籍isbn條碼號給doumibook,來查詢作者和書名。使用douban api v2版接口,獲取書籍信息,douban返回的json數據。代碼片段如下:
import urllib2 import json bookurlbase = 'http://api.douban.com/v2/book/isbn/' DOUBAN_APIKEY = '' #豆瓣上申請的APIKEY def query_book_info(isbn): url = '%s%s?apikey=%s' % (bookurlbase, isbn, DOUBAN_APIKEY) resp = urllib2.urlopen(url) book = json.loads(resp.read()) info = ''.join(book['author']) + ': ' + book['title'] return info
4,消息回復
查詢到書籍信息之后,消息服務器響應微信服務器的消息推送,回復內容結構同樣是XML,假設我們回復文本消息,基本結構如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[content]]></Content> <FuncFlag>0</FuncFlag> </xml>
其中的ToUserName和FromUserName與消息推送中的相關。
構造回復消息代碼片段如下:
def response_msg(recvmsg, content): textTpl = """<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content> <FuncFlag>0</FuncFlag> </xml>""" echostr = textTpl % (recvmsg['FromUserName'], recvmsg['ToUserName'], recvmsg['CreateTime'], recvmsg['MsgType'], content) return echostr
注意到,回復文本消息,內容比較單一,這里可以使用MsgType為“news”的圖文消息格式進行回復,這里不再贅述,請參考官方文檔以及本文末尾給出的源代碼鏈接。
4,綜合
消息推送HTTP POST請求的處理代碼如下:
@app.route('/weixin', methods=['POST']) def weixin_msg(): data = request.data msg = parse_msg(data) content = 'not found' if msg.has_key('Content'): content = query_book_info(msg['Content']) return response_msg(msg, content)
五、總結
本文以doumibook為例,使用Python Flask框架,介紹了在SAE上搭建微信公眾平台賬號消息服務器的基本步驟。
需要看完整代碼的同學,請移步這里:https://github.com/gzb1985/doumibook_weixin。
doumibook目前僅實現了很簡單的功能,還沒有實用的價值,例如輸入“浪潮之巔”,則返回:
僅供參考,拋磚引玉吧。
文章更新記錄:
2013.3.31:微信官方公布的新的用戶訂閱消息方法,見四(2)。
(完)