虽然校园App是个我认为的绝对的好主意,但最近有个也不错的营销+开发的模式出现:微信平台+固定域名服务器。

微信公众平台的运行模式不外两个:

一、机器人模式或称转发模式,将说话内容转发到服务器上完成,拿服务器的回复再一次转发,就完成一次问答谈话。

二、人控模式,一个自然人登陆公众平台上,能直接接触到所有关注者,与之交互,这一定也是最累的。

微信公众平台若是服务号,用来做微网站,省去了登录认证过程。但说白了就是微信定制版的微网站。这我一学期后才搞懂,如果早些弄懂就不会做那么多无用功。

微信公众平台须有正面头像+身份证的照片来实名认证,非常严格。顺便一说,微信公众平台官方说法是偏支持大企而非个人。

服务器(准确的说只是一个引擎)有新浪云SAE,百度云BAE,阿里云AAE

SAE最早,但使用云豆消费,注册只送500个。到现在,BAE允许创建10个应用而不用实名认证,SAE是需实名认证的。还有BAESAE强的就是支持git,虽然两者都支持svn,非常合时,刚好我学习git中,我果断选择BAE。云上建的每个应用可有20个版本,但任一个版都可以并且唯一上线。AAE(阿里云)一直不支持python,很让人失望。

第一阶段:入门——轻轻走过飘过。

下面是用数天时间借鉴前人成果Kingson的《一个用Python和Bottle实现基于微信公众平台APISAE查询豆瓣电影的简单应用》开发的。

这个例子非常适合在用Python的开发人员。经过一番狠狠的折腾,我还弄懂了其它问题:云的概念、OAuth、token、微信API调用,网站API调用、python等,百度谷歌都会有答案。

还有很多像微信API通信认证(话说竞然用xml而不用json通信,不过这是取舍问题,无可厚非),python web框架,git对接云服务器……用了我许多时间。过程曲折复杂,看起来只是转移一下云平台,但实名认证,开发者域名的认证等浪费了我很多时间,因为没经验,很多各种问题都撞上了,尤其是我这种粗心大意,心眼碗粗的人,整个过程实在不算顺利,但我相信别人都会比我顺利,因为我连最低级的错都犯了。不多说,贴上关键代码代码。

下载地址:http://pan.baidu.com/s/1d1g3l

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#! /usr/bin/env python
# coding=utf-8
__author__ = 'jszhou'
from bottle import *
import hashlib
import xml.etree.ElementTree as ET
import urllib2
# import requests
import json

app = Bottle()

"""
Change Log:
03-04--03-08 完成微信API+Python自动回复代码雏形,可以通过电影ID查询电影信息,以Text形式返回给用户电影
Title和电影summary
# 03-11 完成通过电影名称查询并返回图文格式的数据
# 03-13 1.增加给新关注的用户自动返回“欢迎关注豆瓣电影,输入电影名称即可快速查询电影讯息哦!”信息的功能
        2.完善注释信息

关于本地调试问题:
微信没有提供本地调试功能,给用户造成不小的麻烦。
打开Bottle的Debug功能,在本地运行自己的代码(启动Server),使用Chrome或Firefox上的Advanced Rest Client插件来模拟微信服务器向自己的应用发送请求,
这样就可以看到详细的报错信息,方便开发者定位修复问题,其相当于,自己的应用是SAE,而Advanced Rest Client模拟的是新微信客户端和微信服务器。
也有同学自己写脚本,模拟微信服务器发送数据,这也是同样的道理。

遗留问题:
1.从豆瓣拿到的海报图片都是竖向的,而微信中显示的是横向的,所以在微信看图片就被裁了一节,不过还好能看,
  如何能完整显示海报图片,有待进一步research;
2.现在的通过电影名称返回的结果,实际上是拿的豆瓣返回的第一条数据,这样就有可能不准确,如何精确匹配用户的
  查询条件,也还需要进一步研究。
"""

@app.get("/")
def checkSignature():
    """
    这里是用来做接口验证的,从微信Server请求的URL中拿到“signature”,“timestamp”,"nonce"和“echostr”,
    然后再将token, timestamp, nonce三个排序并进行Sha1计算,并将计算结果和拿到的signature进行比较,
    如果相等,就说明验证通过。
    话说微信的这个验证做的很渣,因为只要把echostr返回去,就能通过验证,这也就造成我看到一个Blog中,
    验证那儿只返回了一个echostr,而纳闷了半天。
    附微信Server请求的Url示例:http://yoursaeappid.sinaapp.com//?signature=730e3111ed7303fef52513c8733b431a0f933c7c
&echostr=5853059253416844429&timestamp=1362713741&nonce=1362771581
    """
    token = ""  # 你在微信公众平台上设置的TOKEN
    signature = request.GET.get('signature', None)  # 拼写不对害死人那,把signature写成singnature,直接导致怎么也认证不成功
    timestamp = request.GET.get('timestamp', None)
    nonce = request.GET.get('nonce', None)
    echostr = request.GET.get('echostr', None)
    tmpList = [token, timestamp, nonce]
    tmpList.sort()
    tmpstr = "%s%s%s" % tuple(tmpList)
    hashstr = hashlib.sha1(tmpstr).hexdigest()
    if hashstr == signature:
        return echostr
    else:
        return "wws:indentify error"

def parse_msg():
    """
    这里是用来解析微信Server Post过来的XML数据的,取出各字段对应的值,以备后面的代码调用,也可用lxml等模块。
    """
    recvmsg = request.body.read()  # 严重卡壳的地方,最后还是在Stack OverFlow上找到了答案
    root = ET.fromstring(recvmsg)
    msg = {}
    for child in root:
        msg[child.tag] = child.text
    return msg

def query_movie_info():
    """
    这里使用豆瓣的电影search API,通过关键字查询电影信息,这里的关键点是,一是关键字取XML中的Content值,
    二是如果Content中存在汉字,就需要先转码,才能进行请求
    """
    movieurlbase = "http://api.douban.com/v2/movie/search"
    DOUBAN_APIKEY = ""  # 这里需要填写你自己在豆瓣上申请的应用的APIKEY
    movieinfo = parse_msg()
    searchkeys = urllib2.quote(movieinfo["Content"].encode("utf-8"))  # 如果Content中存在汉字,就需要先转码,才能进行请求
    url = '%s?q=%s&apikey=%s' % (movieurlbase, searchkeys, DOUBAN_APIKEY)
    # return "<p>{'url': %s}</p>" % url
    # url = '%s%s?apikey=%s' % (movieurlbase, id["Content"], DOUBAN_APIKEY)
    # resp = requests.get(url=url, headers=header)
    resp = urllib2.urlopen(url)
    movie = json.loads(resp.read())
    # return "<p>{'movie': %s}</p>" % movie
    # info = movie["subjects"][0]["title"] + movie["subjects"][0]["alt"]
    # info = movie['title'] + ': ' + ''.join(movie['summary'])
    return movie
    # return info

def query_movie_details():
    """
    这里使用豆瓣的电影subject API,通过在query_movie_info()中拿到的电影ID,来获取电影的summary。
    """
    movieurlbase = "http://api.douban.com/v2/movie/subject/"
    DOUBAN_APIKEY = ""  # 这里需要填写你自己在豆瓣上申请的应用的APIKEY
    id = query_movie_info()
    url = '%s%s?apikey=%s' % (movieurlbase, id["subjects"][0]["id"], DOUBAN_APIKEY)
    resp = urllib2.urlopen(url)
    description = json.loads(resp.read())
    description = ''.join(description['summary'])
    return description

@app.post("/")
def response_msg():
    """
    这里是响应微信Server的请求,并返回数据的主函数,判断Content内容,如果是“Hello2BizUser”,就
    表明是一个新注册用户,调用纯文本格式返回,如果是其他的内容就组织数据以图文格式返回。

    基本思路:
    # 拿到Post过来的数据
    # 分析数据(拿到FromUserName、ToUserName、CreateTime、MsgType和content)
    # 构造回复信息(将你组织好的content返回给用户)
    """
    #拿到并解析数据
    msg = parse_msg()
    #设置返回数据模板
    #纯文本格式
    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>"""
    #图文格式
    pictextTpl = """<xml>
                <ToUserName><![CDATA[%s]]></ToUserName>
                <FromUserName><![CDATA[%s]]></FromUserName>
                <CreateTime>%s</CreateTime>
                <MsgType><![CDATA[news]]></MsgType>
                <ArticleCount>1</ArticleCount>
                <Articles>
                <item>
                <Title><![CDATA[%s]]></Title>
                <Description><![CDATA[%s]]></Description>
                <PicUrl><![CDATA[%s]]></PicUrl>
                <Url><![CDATA[%s]]></Url>
                </item>
                </Articles>
                <FuncFlag>1</FuncFlag>
                </xml> """
    #判断Content内容,如果等于"Hello2BizUser",表明是一个新关注用户,如果不是,就返回电影标题,电影简介
    #和电影海报组成的图文信息
    if msg["Content"] == "Hello2BizUser":
        echostr = textTpl % (
            msg['FromUserName'], msg['ToUserName'], str(int(time.time())), msg['MsgType'],
            u"欢迎关注豆瓣电影,输入电影名称即可快速查询电影讯息哦!")
        return echostr
    else:
        Content = query_movie_info()
        description = query_movie_details()
        echostr = pictextTpl % (msg['FromUserName'], msg['ToUserName'], str(int(time.time())),
                                Content["subjects"][0]["title"], description,
                                Content["subjects"][0]["images"]["large"], Content["subjects"][0]["alt"])
        return echostr

if __name__ == "__main__":
    # Interactive mode
    debug(True)
    run(app,host='127.0.0.1', port=8080, reloader=True)
else:
    # Mod WSGI launch
#    import sae
#    debug(True)
#    os.chdir(os.path.dirname(__file__))
#    app = default_app()
#    application = sae.create_wsgi_app(app)

#################################################
    #os.chdir(os.path.dirname(__file__))#Forbidden to access
    from bae.core.wsgi import WSGIApplication
    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, 开发项目澳洲红酒微信服务号,

本人微信公众号pythonwoodpub.jpg 开发项目澳洲红酒微信服务号.jpg



原文出自发表的//blog.pythonwood.com/2013/10/微信公众平台开发(免费云BAE+高效优雅的Python+网站开放的API)/



扩展阅读