最近想在微信上面实现天气查询,分两个步骤:
1.被动回复:输入天气、xx天气获取天气情况
2.主动推送:每天定时推送天气(针对24小时内产生交互的人)
目前已经实现第一个步骤,如下:
现将实现此功能环节、步骤等整理如下:
前置条件:
1.申请微信订阅号
2.申请百度应用引擎(BAE python环境)
技术知识:
1.python
2.Django
其他知识:
1.HTTP协议
2.JSON
3.WechatPy(Python 微信 SDK)
实现思路:
用户上行文字消息 【XX天气】=>订阅号=>微信客户端=>微信后台=>推送到微信开发者接口=>BAE=>Django(Python)=>WechatPy=>文字解析=>百度天气API
=>协议JSON=>生成文章描述XML=>微信后台=>微信客户端 【页面显示】
实现代码:
百度天气查询【输入地市】:
http://api.map.baidu.com/telematics/v3/weather?location=%s&output=json&ak=YOURAK
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #! /usr/bin/env python 2 # coding=utf-8 3 4 import requests 5 import json 6 7 8 class _cityWeather(): 9 10 #key 11 appkey='7ed5edfdb07013922a47bd2fbf194d3d' 12 #url 13 appurl='http://api.map.baidu.com/telematics/v3/weather?location=%s&output=json&ak=7ed5edfdb07013922a47bd2fbf194d3d' 14 15 def __init__(self): 16 pass 17 18 def getcitynamebyip(self,ip): 19 20 pass 21 22 def getcityweather(self,cityname='合肥'): 23 requrl=self.appurl % (cityname,) 24 r = requests.get(requrl) 25 return r.text 26 27 cityweather = _cityWeather() 28 29 30 if __name__ =="__main__": 31 32 33 print cityweather.getcityweather('合肥') 34 35 36 37 38 39 40 41 42
Django :
路由【url.py】:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.conf.urls import patterns, include, url import weixinviews import robot urlpatterns = patterns('', # url(r'^$', weixinviews.handleRequest), url(r'^$', robot.weixin), )
对接微信&消息分流【robot.py】:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding: utf-8 -*- 2 3 4 from django.http import HttpResponse 5 from django.template import RequestContext, Template 6 from django.views.decorators.csrf import csrf_exempt 7 from django.utils.encoding import smart_str, smart_unicode 8 9 from wechatpy import parse_message, create_reply 10 from wechatpy.utils import check_signature 11 from wechatpy.exceptions import InvalidSignatureException 12 from wechatpy.replies import BaseReply 13 14 import reply_text 15 import reply_event 16 17 TOKEN = 'YOURTOKEN' # 注意要与微信公众帐号平台上填写一致 18 19 @csrf_exempt 20 def weixin(request): 21 if request.method == 'GET': 22 signature = request.GET.get('signature', '') 23 timestamp = request.GET.get('timestamp', '') 24 nonce = request.GET.get('nonce', '') 25 echo_str = request.GET.get('echostr', '') 26 try: 27 check_signature(TOKEN, signature, timestamp, nonce) 28 except InvalidSignatureException: 29 echo_str = 'error' 30 response = HttpResponse(echo_str, content_type="text/plain") 31 return response 32 else: 33 reply=None 34 msg = parse_message(request.body) 35 if msg.type == 'text': 36 reply = reply_text.doreply(msg) 37 elif msg.type=='event': 38 reply = reply_event.doreply(msg) 39 else: 40 pass 41 42 43 44 if not reply or not isinstance(reply, BaseReply): 45 reply = create_reply('暂不支持文本消息外的其他操作...\r\n回复:xx天气 查询地市天气情况', msg) 46 47 response = HttpResponse(reply.render() , content_type="application/xml") 48 return response 49
文字消息应答【reply_text.py】:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding: utf-8 -*- 2 3 4 from wechatpy import parse_message, create_reply 5 from wechatpy.replies import TextReply, ArticlesReply 6 from wechatpy.utils import check_signature 7 from wechatpy.exceptions import InvalidSignatureException 8 9 from webcore.weatherservices import cityweather 10 from isay9685.models import CityWeahter 11 import json 12 import time 13 from datetime import datetime 14 15 16 17 def doreply(msg): 18 reply = None 19 20 try: 21 22 if msg.content[-2:] == u'天气': 23 if(len(msg.content) == 2): 24 cityname = '合肥' 25 else: 26 cityname = msg.content[:-2] 27 reply = replyWeather(cityname, msg) 28 else: 29 reply = create_reply(repr(msg), msg) 30 31 except Exception as e: 32 print 'error:', e 33 34 return reply 35 36 def replyWeather(cityname, msg): 37 38 reply = None 39 40 dateid = time.strftime("%Y%m%d") 41 timeid = time.strftime("%H") 42 43 cWeahter = CityWeahter.objects.filter(dateid=dateid, timeid=timeid, cityname=cityname) 44 if cWeahter: 45 weatherstr = cWeahter[0].wheather 46 else: 47 weatherstr = cityweather.getcityweather(cityname) 48 cw = CityWeahter(dateid=dateid, timeid=timeid, cityname=cityname, wheather=weatherstr, createtime=datetime.now()) 49 cw.save() 50 51 # weatherstr = '''{"error":0,"status":"success","date":"2015-06-15","results":[{"currentCity":"合肥","pm25":"126","index":[{"title":"穿衣","zs":"热","tipt":"穿衣指数","des":"天气热,建议着短裙、短裤、短薄外套、T恤等夏季服装。"},{"title":"洗车","zs":"不宜","tipt":"洗车指数","des":"不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。"},{"title":"旅游","zs":"适宜","tipt":"旅游指数","des":"温度适宜,又有较弱降水和微风作伴,会给您的旅行带来意想不到的景象,适宜旅游,可不要错过机会呦!"},{"title":"感冒","zs":"较易发","tipt":"感冒指数","des":"相对今天出现了较大幅度降温,较易发生感冒,体质较弱的朋友请注意适当防护。"},{"title":"运动","zs":"较不宜","tipt":"运动指数","des":"有降水,推荐您在室内进行健身休闲运动;若坚持户外运动,须注意携带雨具并注意避雨防滑。"},{"title":"紫外线强度","zs":"弱","tipt":"紫外线强度指数","des":"紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。"}],"weather_data":[{"date":"周一 06月15日 (实时:27℃)","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/xiaoyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/zhongyu.png","weather":"小雨转中雨","wind":"南风微风","temperature":"28 ~ 22℃"},{"date":"周二","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/dayu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/xiaoyu.png","weather":"大雨转小雨","wind":"北风微风","temperature":"26 ~ 21℃"},{"date":"周三","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/xiaoyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/yin.png","weather":"小雨转阴","wind":"北风微风","temperature":"24 ~ 20℃"},{"date":"周四","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"东北风3-4级","temperature":"28 ~ 20℃"}]}]} ''' 52 weatherjson = json.loads(weatherstr) 53 54 if weatherjson and weatherjson.get('error') == 0: 55 date = weatherjson.get('date') 56 result = weatherjson.get('results')[0] 57 58 currentCity = result.get('currentCity') 59 pm25 = result.get('pm25') 60 61 wheathernowdatas = result.get('weather_data')[0] 62 63 weathermsg = repr(wheathernowdatas) 64 65 reply = ArticlesReply(message=msg) 66 67 # simply use dict as article 68 reply.add_article({ 69 'title': wheathernowdatas.get('date'), 70 }) 71 reply.add_article({ 72 'title': 73 u'%s %s %s' % (wheathernowdatas.get('weather') , wheathernowdatas.get('temperature') , wheathernowdatas.get('wind')) \ 74 + '\r\n'\ 75 + '\r\n'\ 76 + currentCity + u' PM2.5: ' + pm25 , 77 'url': 'http://isay9685.duapp.com' 78 }) 79 reply.add_article({ 80 'title': u'白天', 81 'image': wheathernowdatas.get('dayPictureUrl'), 82 }) 83 reply.add_article({ 84 'title': u'晚上', 85 'image': wheathernowdatas.get('nightPictureUrl'), 86 }) 87 88 89 return reply 90 91
事件消息应答【reply_event.py】:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- coding: utf-8 -*- 2 3 4 from wechatpy import parse_message, create_reply 5 from wechatpy.replies import TextReply, ArticlesReply 6 from wechatpy.utils import check_signature 7 from wechatpy.exceptions import InvalidSignatureException 8 9 from webcore.weatherservices import cityweather 10 from isay9685.models import CityWeahter 11 import json 12 import time 13 from datetime import datetime 14 15 16 17 def doreply(msg): 18 reply = None 19 20 try: 21 22 if msg.event == 'subscribe': 23 reply = replySubscribe(msg) 24 else: 25 reply = create_reply(repr(msg), msg) 26 27 except Exception as e: 28 print 'error:', e 29 reply = None 30 31 return reply 32 33 def replySubscribe(msg): 34 35 36 reply = TextReply(content='欢迎关注RYHAN的微信订阅号,回复XX天气即可查询天气情况。\r\n 如:合肥天气', message=msg) 37 return reply 38 39 40 41 42 43
BAE【requirements.txt】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 django==1.5.2 2 MySQL-python==1.2.4 3 requests==2.8.1 4 #pycrypto==2.6.1 5 PyCrypto 6 #cryptography==0.8.2 7 wechatpy
测试微信号: