雖然校園App是個我認為的絕對的好主意,但最近有個也不錯的營銷+開發的模式出現:微信平台+固定域名服務器。
微信公眾平台的運行模式不外兩個:
一、機器人模式或稱轉發模式,將說話內容轉發到服務器上完成,拿服務器的回復再一次轉發,就完成一次問答談話。
二、人控模式,一個自然人登陸公眾平台上,能直接接觸到所有關注者,與之交互,這一定也是最累的。
微信公眾平台若是服務號,用來做微網站,省去了登錄認證過程。但說白了就是微信定制版的微網站。這我一學期后才搞懂,如果早些弄懂就不會做那么多無用功。
微信公眾平台須有正面頭像+身份證的照片來實名認證,非常嚴格。順便一說,微信公眾平台官方說法是偏支持大企而非個人。
服務器(准確的說只是一個引擎)有新浪雲SAE,百度雲BAE,阿里雲AAE。
SAE最早,但使用雲豆消費,注冊只送500個。到現在,BAE允許創建10個應用而不用實名認證,SAE是需實名認證的。還有BAE比SAE強的就是支持git,雖然兩者都支持svn,非常合時,剛好我學習git中,我果斷選擇BAE。雲上建的每個應用可有20個版本,但任一個版都可以並且唯一上線。AAE(阿里雲)一直不支持python,很讓人失望。
第一階段:入門——輕輕走過飄過。
下面是用數天時間借鑒前人成果Kingson的《一個用Python和Bottle實現基於微信公眾平台API和SAE查詢豆瓣電影的簡單應用》開發的。
這個例子非常適合在用Python的開發人員。經過一番狠狠的折騰,我還弄懂了其它問題:雲的概念、OAuth、token、微信API調用,網站API調用、python等,百度谷歌都會有答案。
還有很多像微信API通信認證(話說競然用xml而不用json通信,不過這是取舍問題,無可厚非),python web框架,git對接雲服務器……用了我許多時間。過程曲折復雜,看起來只是轉移一下雲平台,但實名認證,開發者域名的認證等浪費了我很多時間,因為沒經驗,很多各種問題都撞上了,尤其是我這種粗心大意,心眼碗粗的人,整個過程實在不算順利,但我相信別人都會比我順利,因為我連最低級的錯都犯了。不多說,貼上關鍵代碼代碼。
下載地址:http://pan.baidu.com/s/1d1g3l
1 #! /usr/bin/env python 2 # coding=utf-8 3 __author__ = 'jszhou' 4 from bottle import * 5 import hashlib 6 import xml.etree.ElementTree as ET 7 import urllib2 8 # import requests 9 import json 10 11 app = Bottle() 12 13 """ 14 Change Log: 15 03-04--03-08 完成微信API+Python自動回復代碼雛形,可以通過電影ID查詢電影信息,以Text形式返回給用戶電影 16 Title和電影summary 17 # 03-11 完成通過電影名稱查詢並返回圖文格式的數據 18 # 03-13 1.增加給新關注的用戶自動返回“歡迎關注豆瓣電影,輸入電影名稱即可快速查詢電影訊息哦!”信息的功能 19 2.完善注釋信息 20 21 關於本地調試問題: 22 微信沒有提供本地調試功能,給用戶造成不小的麻煩。 23 打開Bottle的Debug功能,在本地運行自己的代碼(啟動Server),使用Chrome或Firefox上的Advanced Rest Client插件來模擬微信服務器向自己的應用發送請求, 24 這樣就可以看到詳細的報錯信息,方便開發者定位修復問題,其相當於,自己的應用是SAE,而Advanced Rest Client模擬的是新微信客戶端和微信服務器。 25 也有同學自己寫腳本,模擬微信服務器發送數據,這也是同樣的道理。 26 27 遺留問題: 28 1.從豆瓣拿到的海報圖片都是豎向的,而微信中顯示的是橫向的,所以在微信看圖片就被裁了一節,不過還好能看, 29 如何能完整顯示海報圖片,有待進一步research; 30 2.現在的通過電影名稱返回的結果,實際上是拿的豆瓣返回的第一條數據,這樣就有可能不准確,如何精確匹配用戶的 31 查詢條件,也還需要進一步研究。 32 """ 33 34 @app.get("/") 35 def checkSignature(): 36 """ 37 這里是用來做接口驗證的,從微信Server請求的URL中拿到“signature”,“timestamp”,"nonce"和“echostr”, 38 然后再將token, timestamp, nonce三個排序並進行Sha1計算,並將計算結果和拿到的signature進行比較, 39 如果相等,就說明驗證通過。 40 話說微信的這個驗證做的很渣,因為只要把echostr返回去,就能通過驗證,這也就造成我看到一個Blog中, 41 驗證那兒只返回了一個echostr,而納悶了半天。 42 附微信Server請求的Url示例:http://yoursaeappid.sinaapp.com//?signature=730e3111ed7303fef52513c8733b431a0f933c7c 43 &echostr=5853059253416844429×tamp=1362713741&nonce=1362771581 44 """ 45 token = "" # 你在微信公眾平台上設置的TOKEN 46 signature = request.GET.get('signature', None) # 拼寫不對害死人那,把signature寫成singnature,直接導致怎么也認證不成功 47 timestamp = request.GET.get('timestamp', None) 48 nonce = request.GET.get('nonce', None) 49 echostr = request.GET.get('echostr', None) 50 tmpList = [token, timestamp, nonce] 51 tmpList.sort() 52 tmpstr = "%s%s%s" % tuple(tmpList) 53 hashstr = hashlib.sha1(tmpstr).hexdigest() 54 if hashstr == signature: 55 return echostr 56 else: 57 return "wws:indentify error" 58 59 def parse_msg(): 60 """ 61 這里是用來解析微信Server Post過來的XML數據的,取出各字段對應的值,以備后面的代碼調用,也可用lxml等模塊。 62 """ 63 recvmsg = request.body.read() # 嚴重卡殼的地方,最后還是在Stack OverFlow上找到了答案 64 root = ET.fromstring(recvmsg) 65 msg = {} 66 for child in root: 67 msg[child.tag] = child.text 68 return msg 69 70 def query_movie_info(): 71 """ 72 這里使用豆瓣的電影search API,通過關鍵字查詢電影信息,這里的關鍵點是,一是關鍵字取XML中的Content值, 73 二是如果Content中存在漢字,就需要先轉碼,才能進行請求 74 """ 75 movieurlbase = "http://api.douban.com/v2/movie/search" 76 DOUBAN_APIKEY = "" # 這里需要填寫你自己在豆瓣上申請的應用的APIKEY 77 movieinfo = parse_msg() 78 searchkeys = urllib2.quote(movieinfo["Content"].encode("utf-8")) # 如果Content中存在漢字,就需要先轉碼,才能進行請求 79 url = '%s?q=%s&apikey=%s' % (movieurlbase, searchkeys, DOUBAN_APIKEY) 80 # return "<p>{'url': %s}</p>" % url 81 # url = '%s%s?apikey=%s' % (movieurlbase, id["Content"], DOUBAN_APIKEY) 82 # resp = requests.get(url=url, headers=header) 83 resp = urllib2.urlopen(url) 84 movie = json.loads(resp.read()) 85 # return "<p>{'movie': %s}</p>" % movie 86 # info = movie["subjects"][0]["title"] + movie["subjects"][0]["alt"] 87 # info = movie['title'] + ': ' + ''.join(movie['summary']) 88 return movie 89 # return info 90 91 def query_movie_details(): 92 """ 93 這里使用豆瓣的電影subject API,通過在query_movie_info()中拿到的電影ID,來獲取電影的summary。 94 """ 95 movieurlbase = "http://api.douban.com/v2/movie/subject/" 96 DOUBAN_APIKEY = "" # 這里需要填寫你自己在豆瓣上申請的應用的APIKEY 97 id = query_movie_info() 98 url = '%s%s?apikey=%s' % (movieurlbase, id["subjects"][0]["id"], DOUBAN_APIKEY) 99 resp = urllib2.urlopen(url) 100 description = json.loads(resp.read()) 101 description = ''.join(description['summary']) 102 return description 103 104 @app.post("/") 105 def response_msg(): 106 """ 107 這里是響應微信Server的請求,並返回數據的主函數,判斷Content內容,如果是“Hello2BizUser”,就 108 表明是一個新注冊用戶,調用純文本格式返回,如果是其他的內容就組織數據以圖文格式返回。 109 110 基本思路: 111 # 拿到Post過來的數據 112 # 分析數據(拿到FromUserName、ToUserName、CreateTime、MsgType和content) 113 # 構造回復信息(將你組織好的content返回給用戶) 114 """ 115 #拿到並解析數據 116 msg = parse_msg() 117 #設置返回數據模板 118 #純文本格式 119 textTpl = """<xml> 120 <ToUserName><![CDATA[%s]]></ToUserName> 121 <FromUserName><![CDATA[%s]]></FromUserName> 122 <CreateTime>%s</CreateTime> 123 <MsgType><![CDATA[%s]]></MsgType> 124 <Content><![CDATA[%s]]></Content> 125 <FuncFlag>0</FuncFlag> 126 </xml>""" 127 #圖文格式 128 pictextTpl = """<xml> 129 <ToUserName><![CDATA[%s]]></ToUserName> 130 <FromUserName><![CDATA[%s]]></FromUserName> 131 <CreateTime>%s</CreateTime> 132 <MsgType><![CDATA[news]]></MsgType> 133 <ArticleCount>1</ArticleCount> 134 <Articles> 135 <item> 136 <Title><![CDATA[%s]]></Title> 137 <Description><![CDATA[%s]]></Description> 138 <PicUrl><![CDATA[%s]]></PicUrl> 139 <Url><![CDATA[%s]]></Url> 140 </item> 141 </Articles> 142 <FuncFlag>1</FuncFlag> 143 </xml> """ 144 #判斷Content內容,如果等於"Hello2BizUser",表明是一個新關注用戶,如果不是,就返回電影標題,電影簡介 145 #和電影海報組成的圖文信息 146 if msg["Content"] == "Hello2BizUser": 147 echostr = textTpl % ( 148 msg['FromUserName'], msg['ToUserName'], str(int(time.time())), msg['MsgType'], 149 u"歡迎關注豆瓣電影,輸入電影名稱即可快速查詢電影訊息哦!") 150 return echostr 151 else: 152 Content = query_movie_info() 153 description = query_movie_details() 154 echostr = pictextTpl % (msg['FromUserName'], msg['ToUserName'], str(int(time.time())), 155 Content["subjects"][0]["title"], description, 156 Content["subjects"][0]["images"]["large"], Content["subjects"][0]["alt"]) 157 return echostr 158 159 if __name__ == "__main__": 160 # Interactive mode 161 debug(True) 162 run(app,host='127.0.0.1', port=8080, reloader=True) 163 else: 164 # Mod WSGI launch 165 # import sae 166 # debug(True) 167 # os.chdir(os.path.dirname(__file__)) 168 # app = default_app() 169 # application = sae.create_wsgi_app(app) 170 171 ################################################# 172 #os.chdir(os.path.dirname(__file__))#Forbidden to access 173 from bae.core.wsgi import WSGIApplication 174 application = WSGIApplication(app)
注:代碼中用json.dumps會更好。 后注:此注不對。
深入階段,將http://www.cnblogs.com/mchina/tag/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97/里的功能用python實現之。
……
本代碼需要與bottle.py一並上傳到服務器空間。
微信開發調試小工具下載:http://www.cnblogs.com/linkbiz/archive/2013/05/16/3080306.html
附上一些特別信息:
本人微信公眾號pythonwoodpub, 開發項目澳洲紅酒微信服務號,
,
