Python+SAE微信公众平台转发

Python+SAE微信公众平台转发

🗨

     随着移动互联网时代的到来,微信——一个改变着我们生活的产品悄悄走近了我们的生活。我们不得不觉得自己很幸运,自己能在这个世界上遇到像QQ、微博、微信这样优秀的产品,同时,也不得不感叹这些产品的强大之处。就拿微信来说吧,我们可以文字聊天、发语音、报告位置、甚至是发视频、对讲机等功能,确实为我们平时的沟通大大降低了成本,譬如以前生日祝福等都是打电话、发短信,可在微信时代我们只要发文字或者是发一段语音就好了——省钱、简单、好用。

     移动互联网的时代,你不一定需要你自己的移动APP,而且自己外包给别人做成本也比较高,小小的创业公司,就算你有了自己的移动APP,还要自己去发展用户(不同性质的公司需求不一样,举个例子而已),而且还要花大量的时间和精力在移动APP的维护上。基于不同的平台需要不同的人开发,譬如安卓版、IOS版、WP版等。因此,选择在微信上做二次开发是中小企业的不二选择。也有不少企业也有了自己的公众账号,甚至是服务号。

 

SAE创建python程序,在index.wsgi编写以下代码,在微信验证输入xxx.sinaapp.com,token任意,完成验证,微信中回复hello

#utf-8

import sae

import urlparse

import xml.etree.ElementTree as ET 

def app(environ, start_response):

    status = '200 OK'

    response_headers = [('Content-type', 'text/html; charset=utf-8')]

    start_response(status, response_headers)

    method=environ['REQUEST_METHOD']

    if method=="GET":

        query=environ['QUERY_STRING']

        echostr=urlparse.parse_qs(query)['echostr']

        return echostr

    elif method=="POST":

        post=environ['wsgi.input']        

        root = ET.parse(post)

        fromUser=root.findtext(".//FromUserName")

        toUser=root.findtext(".//ToUserName")

        CreateTime=root.findtext(".//CreateTime")

        texttpl='''<xml>

        <ToUserName>'''+fromUser+'''</ToUserName>

        <FromUserName>'''+toUser+'''</FromUserName>

        <CreateTime>'''+CreateTime+'''</CreateTime>

        <MsgType><![CDATA[text]]></MsgType>

        <Content><![CDATA[hello]]></Content>

        </xml>'''

        return texttpl 

application = sae.create_wsgi_app(app)

 

 

巧用SAE搭建微信本地调试环境

 

 

 

       基于公众账号的二次开发,楼主选择的是最简单易懂的Python语言,结合Python一个轻量级的框架Django开发,操作比较简单,简单两下就能学会。由于业务发展需要,最近楼主也为自己所在的公司从事微信相关开发。因此将开发中遇到的问题及解决方案总结出来与大家,共同进步、共同成长。

      需求描述:

      楼主所在的公司是一家微型金融机构,有自己的网站,现在要将网站上的一部分功能(譬如放贷、购买理财产品等)在微信端实现,这样方便微信用户的操作,顺便也从微信用户中导一份流量到自己的网站。

      首先,我们来了解一下微信通信的原理。现在假设你也接到和楼主相同的需求。举个简单的流程:在微信端实现放贷的流程。                第一步,用户在微信端发送放贷请求到微信服务器,请求格式由你自己决定,只要能解析就行;

            第二步,微信服务器接受到请求信息,将信息转发到你自己的网站;

            第三步,在你自己的网站处理完业务逻辑,反馈结果发送到微信服务器;

            第四步,微信服务器将反馈结果转发给用户。

            简单四步,完成了整个通讯,详细的流程如下图:

      上图中,已经清晰的描述了通讯的原理,但是在开发过程中,我们大多都在本地,一般都是局域网,怎样才能和微信服务器实现通讯呢?当然你可以选择在服务器提供商买一台服务器,在那上面做开发。不过,细细盘算下来,成本确实有点高,所以我们要选择最简便有效的方法来解决我们的问题。在这里,楼主用到了SAE,当然你也可以用GAE, 在本地(局域网内)搭建了一个开发环境。当然,其原理还是跟上图很类似,只是稍微复杂了一点点:

 

            第一步,用户在微信端发送放贷请求到微信服务器;

            第二步,微信服务器接受到请求信息,将信息转发SAE(需要自己配置);

            第三步,SAE将请求转发到指定位置(需要自己配置,还需要提供域名),我们暂且命名为“中转站”;

            第四步,指定位置(“中转站”)将请求转发到你的路由;

            第五步,路由通过映射转发到你的内网IP(即你的本地服务器);

            第六步,你本地服务器处理完成后发送反馈到路由器;

            第七步,路由器转发到指定位置(中转站);

            第八步,指定位置(中转站)转发到SAE;

            第九步,SAE转发到微信服务器;

            第十步,微信服务器将反馈结果转发给用户。

         看起来,确实有一点点复杂,不急,先看看图,且听我细细道来。

  

    具体思路我们已经很清楚了,现阶段,我们只需要解决各个节点中的问题即可了。

    我们从简单的开始:

     (1) 配置路由器映射。可能不是所有的路由器都有这个功能,其目的是为了在外网能访问你本地的IP。一般在你上网的时候,都会对应有一个IP地址,可以通过在百度中输入IP来查询你的外网IP(见下图)。由于外网IP没有给用户开放80端口(如果开放了大家的机器都可以当服务器用了),所以你需要自己打开一个端口,让外网可以访问。我在本地我指定的是9000端口,假设我现在的IP地址是192.168.0.99,外网IP经查得是116.77.5.229,那么当你本地开启网络服务后,通过访问116.77.5.229:9000地址,就可以和你本机进行通信了。

     (2) 配置“中转站”。通常,我们上网的 外网IP都是随机分配的,也就是说,我们每次上网时的外网IP都是不一样的,所以在与微信端通信的时候就比较麻烦了。在这里我们用到的"中转站"就是为了解决这一问题——让你的域名可以顺利解析到你本地的外网IP。因此,我们需要找一个DNS供应商,在这里,楼主找到了免费的DNSPod:https://www.dnspod.cn/ ,不过需要你在这上面注册一个用户,然后将你在域名提供商购买的域名配置为DNSPod的DNS地址,如图:

     除此之外,我们还要配置一个子域名如:wx.xxxx.com(xxxx是你的域名),记录值那一栏就是你外网的IP,但是这样你每次都要查询你的外网IP,然后将IP更改过来来。是不是很繁琐?有没有一劳永逸的办法?

     答案是:当然有。楼主整来了下面一段代码,将你在DNSPod上的用户名和密码写入,在本地配上Python环境就可以使用喔:

 
  1. #!/usr/bin/env python  
  2. # coding: utf-8  
  3.   
  4. import httplib, urllib, urllib2  
  5. import time  
  6. import sys,os  
  7. import re  
  8. import json  
  9.   
  10. username = 'yima1006@qq.com'  #账号  
  11. password = 'password'  #密码  
  12. format = 'json'  
  13.   
  14. domain = [u'wx.xxxxx.com']  #要解析的域名  
  15.   
  16. def get_domain_info(domain):  
  17.     domain_split = domain.split('.')  
  18.     domain_split_len = len(domain_split)  
  19.     maindomain = domain_split[domain_split_len - 2] + '.' + domain_split[domain_split_len - 1]  
  20.     return maindomain,domain  
  21.   
  22. params = {'login_email':username,'login_password':password,'format':format}  
  23.   
  24. def request(action, params, method = 'POST'):  
  25.     headers = {"Content-type""application/x-www-form-urlencoded""Accept""text/json"}  
  26.     conn = httplib.HTTPSConnection("dnsapi.cn")  
  27.     conn.request(method, '/' + action, urllib.urlencode(params), headers)  
  28.     response = conn.getresponse()  
  29.     data = response.read()  
  30.     conn.close()  
  31.     if response.status == 200:  
  32.         return data  
  33.     else:  
  34.         return None  
  35.   
  36. def get_my_domain_id():  
  37.     data = request('Domain.List',params)  
  38.     data = json.loads(data)  
  39.     domainlist = data.get('domains')  
  40.     domaninfo = {}  
  41.     for d in domainlist:  
  42.         domaninfo[d.get('name')]  = d.get('id')  
  43.     return domaninfo  
  44.   
  45. def get_my_domain_record_id(domain_id):  
  46.     params['domain_id'] = domain_id  
  47.     data = request('Record.List',params)  
  48.     data = json.loads(data)  
  49.     if data.get('code') == '10':  
  50.         return None  
  51.     domainname = data.get('domain').get('name')  
  52.     record_list = data.get('records')  
  53.     record = {}  
  54.     for r in record_list:  
  55.         if r.get('type') == 'A':  
  56.             key = r.get('name') != '@' and r.get('name') + '.' + domainname or domainname  
  57.             record[key] = {'id':r.get('id'),'value':r.get('value')}  
  58.     print record  
  59.     return  record  
  60.   
  61. def changerecord(domain,domain_id,record_id,ip):  
  62.     params['domain_id'] = domain_id  
  63.     params['record_id'] = record_id  
  64.     params['record_type'] = 'A'  
  65.     params['record_line'] = '默认'  
  66.     params['sub_domain'] = domain  
  67.     params['ttl'] = 600  
  68.     params['value'] = ip  
  69.     data = request('Record.Modify',params)  
  70.   
  71. def getip():  
  72.     url = 'http://iframe.ip138.com/ic.asp'  
  73.     response = urllib2.urlopen(url)  
  74.     text = response.read()  
  75.     ip = re.findall(r'\d+.\d+.\d+.\d+', text)  
  76.     print '外网IP:%s'%ip[0]  
  77.     return ip[0or None  
  78.   
  79. def updatedomaininfo(domain):  
  80.     m,sub_m = get_domain_info(domain)  
  81.     domain_id = my_domain_id_list.get(m)  
  82.     record_list = get_my_domain_record_id(domain_id)  
  83.     if record_list == None:  
  84.         return None  
  85.     rocord_info = record_list.get(sub_m)  
  86.     record_ip = rocord_info.get('value')  
  87.     record_id = rocord_info.get('id')  
  88.     return sub_m,record_ip,record_id,domain_id  
  89.   
  90. if __name__ == '__main__':  
  91.     my_domain_id_list = get_my_domain_id()  
  92.     try:  
  93.         for dm in domain:  
  94.             domaindata = updatedomaininfo(dm)  
  95.             if domaindata == None:  
  96.                 continue  
  97.             dnsdomain,dnsdmainip,record_id,domain_id = domaindata  
  98.             domain_name = dnsdomain.split('.')[0]  
  99.             ip = getip()  
  100.             if ip == dnsdmainip:  
  101.                 continue  
  102.             else:  
  103.                 changerecord(domain_name,domain_id,record_id,ip)  
  104.     except:  
  105.         pass  

 

       这样,通过访问http://wx.xxxx.com:9000端口就能找到你本地的服务器了。

      

      (3)配置SAE应用。在SAE上,我们只需要做一个简单的代理就行,即将微信服务器转发过来的请求按照原来的方式转发到你的“中转站”就好了。在这里,我们用到了一个Python机器轻量级的web框架——web.py。我们可以在SAE上直接创建一个Python项目,然后在index.wsgi文件中贴如如下一段代码就行了。

 
  1. #/usr/bin/python  
  2. #coding:utf-8  
  3.   
  4. import sys  
  5. reload(sys)  
  6. sys.setdefaultencoding('utf-8')  
  7. import web  
  8. import urllib2  
  9. try:  
  10.     import sae  
  11. except:  
  12.     pass  
  13.   
  14. urls = (  
  15.     '/weixin/?''weixin',  
  16. )  
  17.   
  18. web.config.debug = True  
  19. app = web.application(urls, globals())  
  20.   
  21. class weixin:  
  22.     def GET(self):  
  23.     """ GET 请求转发"""  
  24.         url = "http://wx.xxxx.com:9000/weixin/?"    # 转发地址,与你的路由器端口映射  
  25.         d = web.input()        # 读取参数  
  26.         data = []  
  27.         for i in d:  
  28.             data.append(i + '=' + d[i])  
  29.         url = url + '&'.join(data)      # 拼接参数  
  30.         req = urllib2.Request(url=url)  
  31.         res = urllib2.urlopen(req )  
  32.         return res.read()  
  33.   
  34.     def POST(self):  
  35.     """ POST请求转发 """  
  36.         data = web.data()     # 转发参数  
  37.         url = "http://wx.xxxx.com:9000/weixin/?"     # 转发地址,与你的路由器端口映射  
  38.         req = urllib2.Request(url=url, data=data)  
  39.         res = urllib2.urlopen(req, timeout=10 )  
  40.         return res.read()  
  41.   
  42.   
  43. if __name__ =="__main__":  
  44.     app.run()  
  45. else:  
  46.     try:  
  47.         app = web.application(urls, globals()).wsgifunc()  
  48.         application = sae.create_wsgi_app(app)  
  49.         #application = sae.create_wsgi_app(ShellMiddleware(app))  
  50.     except:  
  51.         pass  

 

     这样,我们本地的开发环境搭建已经基本完成,只需要稍作配置就可以开发了。


频道:Python