微信公眾平台開發(免費雲BAE+高效優雅的Python+網站開放的API)


雖然校園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&timestamp=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,              開發項目澳洲紅酒微信服務號,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM