微信公众号 + python + flask + sae 开发后记

9,593 阅读6分钟
原文链接: www.jianshu.com
  • 最近实名认证了新浪云,送了三百云豆,本来以为可以长期有效,但是没想到坑爹的新浪云即使没有使用,居然也每天自动扣除云豆。这也就解释了为什么我首次开通时候送的两百颗在没有使用的情况下平白无故消失的原因。
  • 言归正传,也正是新浪云的每天扣豆促使了我这种早有想法却一直懒癌发作的人花了一周来写了个微信公众号功能的开发,使用python+flask写了一个简单的传递消息的页面并部署在sae(新浪云)上来实现功能的发送与回复,用了我在公司实习的一周时间,写下这篇文章记录一下前因后果。
  • 要开发公众号,首先你得有个公众号对吧,不会的滚去面壁,很简单,去微信公众平台申请一下,然后绑定一下管理员微信号,再登录官网成为开发者设置一下基本配置,如图,url是你sae应用创建后的url,创建之后直接复制过来就可以了,token是自己设置的,目的是为了验证是不是你自己在使用,听说是出于安全考虑,然并卵,但是后面开发中对接的时候会用到,然后就可以进行微信公众号开发者了。





  • 接下来是去sae新建一个web应用,选的是创建云应用,目前只支持python2.7版本。


  • 自己填好二级域名之后和应用名称之后就创建好了,域名可以随便填,只要不和别人已创建的冲突就行。然后这个域名就是上文中微信开发中需要的url直接搬过去就可以啦。
  • 然后接下来就是代码事情了,我使用的是git,如图,选择GIt之后上传代码即可。










  • 代码中有一共三个文件,功能比较多的话可以自行扩展,在此仅开发了部分功能,一个文件已足够写入功能模块。两个配置文件中的信息如图所示,一个申明一下名字和版本信息,另一个引入sae,然后从代码中引入flask实例app,然后使用sae创建一下即可。
  • 本次实现的功能有天气查询、讲个笑话还有有道翻译,本来网上还有实现机器人对话的实现实例,但是我也试了一下,查到网络上有那种可以直接一键对接的,根本不需要自己开发的,所以觉得没意思,就放弃这个功能了。
  • 其实以上功能的实现也并没有多大难度,因为都有现成的api直接调用,自己只需对接然后实现一下数据的格式化就可以了。本次实验主要体验一下微信公众号开发的具体实现过程,并没有花太多时间在更多功能的实现,而且新浪云每天都在扣云豆,很快就会失效,所以仅做学习交流。
  • 首先是理解一下数据的具体传输过程,其实flask就起了个传递值的作用。




  • python具体对接实现。
    @app.route('/',methods=['GET','POST'])
    def wechat():
    if request.method=='GET':
    token='你的token'
    data=request.args
    signature=data.get('signature','')
    timestamp=data.get('timestamp','')
    nonce =data.get('nonce','')
    echostr=data.get('echostr','')
    s=[timestamp,nonce,token]
    s.sort()
    s=''.join(s)
    if(hashlib.sha1(s).hexdigest()==signature):
    return make_response(echostr)
    else:
    rec=request.stream.read()
    xml_rec=ET.fromstring(rec)
    tou = xml_rec.find('ToUserName').text
    fromu = xml_rec.find('FromUserName').text
    content = xml_rec.find('Content').text
    xml_rep = "%s0"
  • 功能函数的判断
if content.lower()=='joke':#笑话功能
            response = make_response(xml_rep % (fromu,tou,str(int(time.time())),joke()))
            response.content_type='application/xml'
            return response
        elif 'tianqi' in content.lower():#天气功能判断,实现汉字查询天气
            if type(content).__name__ == "unicode":
                content = content.encode('UTF-8')
                place=content.lower().replace('+tianqi','')
                response = make_response(xml_rep % (fromu,tou,str(int(time.time())),weather(place)))
                response.content_type='application/xml'
                return response
            else:
                place=content.lower().replace('+tianqi','')
                response = make_response(xml_rep % (fromu,tou,str(int(time.time())),weather(place)))
                response.content_type='application/xml'
                return response
        else:
            if type(content).__name__ == "unicode":#实现汉译英
                content = content.encode('UTF-8')
                new_word=youdao(content)
                response = make_response(xml_rep % (fromu,tou,str(int(time.time())),new_word))
                response.content_type='application/xml'
                return response
            else:
                new_word=youdao(content)
                response = make_response(xml_rep % (fromu,tou,str(int(time.time())),new_word))
                response.content_type='application/xml'
                return response
  • 公众号发送和回复的消息格式都是xml格式,开发者文档中有具体的解释,发送和接收消息格式如下,然后使用python中的xml.etree.ElementTree提取数据,传递给第三方调用的API,处理后返回值,再继续转化成xml格式经web响应返回给微信。




  • 接下来就是具体功能函数的实现了,首先要有有道的API key和百度API store的账号才能调用,去有道官网申请一下就可以,百度API store只需要有个百度账号就可以了,API key在直接复制一下就可以用了。
  • 翻译和笑话因为使用json格式比较方便,直接解析后获取字典的值就可以了,然后返回格式按个人喜好设计。天气的返回值虽然也是json格式,但是因为他里面套了多层字典,一层一层找比较麻烦,所以就使用了正则表达式简单粗暴一点提取数据,然后自己写一下返回格式,再套入xml格式中返回给微信用户。
  • 代码:
#-*- coding: UTF-8 -*-
import time
import urllib2
from flask import Flask,g,request,make_response
import hashlib
import xml.etree.ElementTree as ET
import json
import random
import re
import urllib

app=Flask(__name__)
app.debug=True

def youdao(word):
quary=urllib2.quote(word)
baseurl=r'http://fanyi.youdao.com/openapi.do?keyfrom=自己的&key=自己的&type=data&doctype=json&version=1.1&q='
url=baseurl+quary
resp=urllib2.urlopen(url)
data=json.loads(resp.read())
if data['errorCode'] == 0:
if 'basic' in data.keys():
trans=u'%s:\n%s\n%s\n网络释意:\n%s'%(data['query'],''.join(data['translation']),' '.join(data['basic']['explains']),''.join(data['web'][0]['value']))
return trans
else:
trans =u'%s:\n基本翻译:%s\n'%(data['query'],''.join(data['translation']))
return trans
elif data['errorCode'] == 20:
return u'对不起,要翻译的文本过长'
elif data['errorCode'] == 30:
return u'对不起,无法进行有效的翻译'
elif data['errorCode'] == 40:
return u'对不起,不支持的语言类型'
else:
return u'对不起,你输入的单词%s无法翻译,请检查拼写'% word

def joke():
try:
for i in random.sample(range(335),1):
page='page='+str(i)
full_url='http://apis.baidu.com/showapi_open_bus/showapi_joke/joke_text?'+page
req = urllib2.Request(full_url)
req.add_header("apikey", "自己的key")
resp = urllib2.urlopen(req,timeout=5)
data= json.loads(resp.read())
for i in random.sample(range(len(data['showapi_res_body']['contentlist'])),1):
return data['showapi_res_body']['contentlist'][i]['title']+'\n'+data['showapi_res_body']['contentlist'][i]['text']+'\n'
except Exception:
return u'抱歉,你运气不好,没有人愿意给你讲笑话,请重试。'

def weather(city_name):
str_city='city='+str(city_name)
url='http://apis.baidu.com/heweather/weather/free?'+str_city
req = urllib2.Request(url)
req.add_header("apikey", "自己的key")
resp = urllib2.urlopen(req)
content= resp.read().decode('utf-8')
city=re.findall(r'"city":"(.*?)"',content,re.S)
update=re.findall(r'"loc":"(.*?)"',content,re.S)
txt=re.findall(r'"txt_d":"(.*?)"',content,re.S)
max_t=re.findall(r'"max":"(.*?)"',content,re.S)
min_t=re.findall(r'"min":"(.*?)"',content,re.S)
hum=re.findall(r'"hum":"(.*?)"',content,re.S)
pcpn=re.findall(r'"pcpn":"(.*?)"',content,re.S)
vis=re.findall(r'"vis":"(.*?)"',content,re.S)
wind_dir=re.findall(r'"dir":"(.*?)"',content,re.S)
wind_sc=re.findall(r'"sc":"(.*?)"',content,re.S)
wind_spd=re.findall(r'"spd":"(.*?)"',content,re.S)
return u'城市:'+city[0]+'\n'+u'更新时间:'+update[0]+'\n'+u'天气情况:'+txt[0]+'\n'+u'最高温度:'+max_t[0]+u'度'+'\n'+u'最低温度:'+min_t[0]+u'度'+'\n'+u'相对湿度:'+hum[0]+'%'+'\n'+u'降水量:'+pcpn[0]+'mm'+'\n'+u'能见度:'+vis[0]+'km'+'\n'+u'风向:'+wind_dir[0]+'\n'+u'风力:'+wind_sc[0]+'\n'+u'风速:'+wind_spd[0]+'km/h'+'\n'
  • 至此,功能已全部实现,使用git上传代码到新浪云之后就可以使用啦。下面是使用演示,公众号应该过不了多久功能就会失效,可恶的sae,就不贴出啦。对于回复图片功能和语音功能没有继续深入,有兴趣的小伙伴可以自行研究。回复语音消息基本原理与文本消息类似,但是还需要调用一下微信另外的接口先返回一个media_id,然后再传递。




  • 最后,花了半天时间写下这篇文章,以此记录学习和生活的点滴,第一次写博文,还有点小激动。不足之处,恳请指正。