每天户外运动,想有个能自动查询天气的方式,然而单位地方比较偏,周边没有气象部门站点,最近的有天气预报的地方也是三十多公里外的城区了。那就DIY一个呗。
某宝上找了找有各种模块,最近又学习了python,很快就有了思路。先做个硬件定时采集气象数据发送到服务端,服务端收到数据记录下以供查找中。结合微信的公众号接口,响应公众号查询请求。
硬件部分参考另一篇博文【气象信息采集装置】 http://www.straka.cn/blog/meteorological_info_collecter/
服务端
微信订阅号对服务器端有约束,必须是80端口,收发均封装成XML格式,微信官网提供了xml解析封装的示例代码,直接使用就可以。
服务器端由python搭建,引用web模块监听80端口,同时服务端监听另一个端口,气象采集端将采集到的气象数据发送至该端口,服务端接收后按格式处理好并记录。
用户在订阅号后台发送消息后,微信服务器会封装成xml消息转发至python服务端,服务端对80端口传入的xml请求解析并获取请求用户等信息,并提取最近一次采集到的气象数据,根据请求封装成规定的xml格式返回微信后台,由微信后台发送至用户端。
除天气查询外的其他功能都是通过python服务端直接处理并响应。
python端代码如下:
该代码包含
main.py:程序入口
# -*- coding: utf-8 -*- # filename: main.py import web from handle import Handle from user import User import sys reload(sys) sys.setdefaultencoding('utf8') urls = ( '/wx', 'Handle', ) if __name__ == '__main__': User.__init__() app = web.application(urls, globals()) app.run()
basic.py:按照微信接口进行相关的验证
# -*- coding: utf-8 -*- # filename: basic.py import urllib import time import json class Basic: def __init__(self): self.__accessToken = '' self.__leftTime = 0 def __real_get_access_token(self): appId = "------" appSecret = "------" postUrl = ("https://api.weixin.qq.com/cgi-bin/token?grant_type=" "client_credential&appid=%s&secret=%s" % (appId, appSecret)) urlResp = urllib.urlopen(postUrl) urlResp = json.loads(urlResp.read()) self.__accessToken = urlResp['access_token'] self.__leftTime = urlResp['expires_in'] def get_access_token(self): if self.__leftTime < 10: self.__real_get_access_token() return self.__accessToken def run(self): while(True): if self.__leftTime > 10: time.sleep(2) self.__leftTime -= 2 else: self.__real_get_access_token()
其中run函数无限循环驻留,不断重复与微信服务器连接,获取访问令牌。
receive.py:对来自微信服务器的信息进行解析,主要是从xml格式中提取信息
# -*- coding: utf-8 -*- # filename: receive.py import xml.etree.ElementTree as ET def parse_xml(web_data): if len(web_data) == 0: return None xmlData = ET.fromstring(web_data) msg_type = xmlData.find('MsgType').text if msg_type == 'text': return TextMsg(xmlData) elif msg_type == 'image': return ImageMsg(xmlData) elif msg_type == 'event': return EventMsg(xmlData) class Msg(object): def __init__(self, xmlData): self.ToUserName = xmlData.find('ToUserName').text self.FromUserName = xmlData.find('FromUserName').text self.CreateTime = xmlData.find('CreateTime').text self.MsgType = xmlData.find('MsgType').text class TextMsg(Msg): def __init__(self, xmlData): Msg.__init__(self, xmlData) self.Content = xmlData.find('Content').text.encode("utf-8") self.MsgId = xmlData.find('MsgId').text class ImageMsg(Msg): def __init__(self, xmlData): Msg.__init__(self, xmlData) self.PicUrl = xmlData.find('PicUrl').text self.MediaId = xmlData.find('MediaId').text self.MsgId = xmlData.find('MsgId').text class EventMsg(Msg): def __init__(self,xmlData): Msg.__init__(self,xmlData) self.Event = xmlData.find('Event').text
parse_xml对传入xml字符串解析后根据其中标示的消息类型分别处理。
reply.py:receive的对应反向操作,对要发送的信息按照微信约定封装并提交至微信服务器
# -*- coding: utf-8 -*- # filename: reply.py import time class Msg(object): def __init__(self): pass def send(self): return "" class TextMsg(Msg): def __init__(self, toUserName, fromUserName, content): self.__dict = dict() self.__dict['ToUserName'] = toUserName self.__dict['FromUserName'] = fromUserName self.__dict['CreateTime'] = int(time.time()) self.__dict['Content'] = content def send(self): XmlForm = """ <xml> <ToUserName><![CDATA[{ToUserName}]]></ToUserName> <FromUserName><![CDATA[{FromUserName}]]></FromUserName> <CreateTime>{CreateTime}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{Content}]]></Content> </xml> """ return XmlForm.format(**self.__dict) class ImageMsg(Msg): def __init__(self, toUserName, fromUserName, mediaId): self.__dict = dict() self.__dict['ToUserName'] = toUserName self.__dict['FromUserName'] = fromUserName self.__dict['CreateTime'] = int(time.time()) self.__dict['MediaId'] = mediaId def send(self): XmlForm = """ <xml> <ToUserName><![CDATA[{ToUserName}]]></ToUserName> <FromUserName><![CDATA[{FromUserName}]]></FromUserName> <CreateTime>{CreateTime}</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[{MediaId}]]></MediaId> </Image> </xml> """ return XmlForm.format(**self.__dict)
封装文本和图片消息。
以上四部分微信官方都提供了样例,直接下载引用即可。
Handle.py是功能实现的主要地方,按照web模块application函数要求提供了针对Get和Post方法的请求响应方法,微信的消息都是通过POST传输的,所以重点是post处理函数。Post里对xml解析处理的文本进行判断,根据文本头进行不同的操作。
# -*- coding: utf-8 -*- # filename: main.py import hashlib import web import reply import receive import time import datetime from user import User from getweather import weather import re import json class Handle(object): def __init__(self): self.strMenu = '回复"1"查看马道桥天气\n回复"2"查看今日伙食\n回复"3"查看今日班车\n' self.strMenu += '回复"4"维修报备\n回复"5"寻医问药\n回复"6"意见建议\n回复"7"设置姓名' def GET(self): try: data = web.input() if len(data) == 0: return "Website not found!" signature = data.signature timestamp = data.timestamp nonce = data.nonce echostr = data.echostr token = "------" list = [token, timestamp, nonce] list.sort() sha1 = hashlib.sha1() map(sha1.update, list) hashcode = sha1.hexdigest() print "handle/GET func: hashcode, signature: ", hashcode, signature if hashcode == signature: return echostr else: return "" except Exception, Argument: return Argument def POST(self): try: webData = web.data() print "Handle Post webdata is \n", webData #后台打日志 recMsg = receive.parse_xml(webData) if isinstance(recMsg, receive.Msg): if recMsg.MsgType == 'text': toUser = recMsg.FromUserName fromUser = recMsg.ToUserName content = "" if recMsg.Content.startswith("1"): recContlow = recMsg.Content.lower() wget = weather() if recContlow.startswith("1tz") or recContlow.startswith("1北京"): content = wget.GetWeather('d7_beijing') self.WriteToLog("GW:"+User.GetUserName(recMsg.FromUserName)+":查询北京天气") elif recContlow.startswith("1xa") or recContlow.startswith("1西安"): content = wget.GetWeather('d7_xian') self.WriteToLog("GW:"+User.GetUserName(recMsg.FromUserName)+":查询西安天气") else: with open("weather.txt", 'rt') as weatherinfo: User.weatherdic = json.load(weatherinfo) lastUpdateTime = time.mktime(time.strptime(User.weatherdic["update"], "%Y-%m-%d %H:%M:%S")) timeNow = time.time() + 13 * 60 * 60 if timeNow - lastUpdateTime > 3600: content = wget.GetWeather() else: content = User.GetWeather() self.WriteToLog("GW:"+User.GetUserName(recMsg.FromUserName)+":查询马道天气") elif recMsg.Content.startswith("2") or recMsg.Content.startswith("菜谱"): content = self.GetCookBook() self.WriteToLog("GC:"+User.GetUserName(recMsg.FromUserName)+":获取菜谱") elif recMsg.Content.startswith("3") or recMsg.Content.startswith("班车"): content = "该功能尚未开通,敬请期待" self.WriteToLog("GB:" + User.GetUserName(recMsg.FromUserName) + ":查询班车") elif recMsg.Content.startswith("4"): content = self.RepairReport(recMsg) elif recMsg.Content.startswith("5"): content = self.MedicineReport(recMsg) elif recMsg.Content.startswith("6"): content = self.SuggestFeedback(recMsg) elif recMsg.Content.startswith("7"): content = self.SetName(recMsg) elif recMsg.Content.startswith("wset:"): content = User.SetWeather(recMsg.Content) self.WriteToLog("WSET:"+User.GetUserName(recMsg.FromUserName)+":设置天气:"+ recMsg.Content[5:]) elif recMsg.Content.startswith("logget:"): content = self.GetLogReport() elif recMsg.Content.startswith("norecv:"): print "rec" content = self.SetLogNotReceive() else: content = self.strMenu if content == "": content = "公众号后台繁忙,请稍后再试" print "reply is :\n" + content replyMsg = reply.TextMsg(toUser,fromUser,content) return replyMsg.send() elif recMsg.MsgType == 'event': toUser = recMsg.FromUserName fromUser = recMsg.ToUserName content = "" if recMsg.Event == "subscribe": content = "感谢您的订阅,这里是----,竭诚为您服务\n\n" + self.strMenu replyMsg = reply.TextMsg(toUser,fromUser,content) return replyMsg.send() else: return "" else: return "" except Exception, Argment: return Argment def WriteToLog(self,content): try: f=open(r"syslog.txt","a") f.write(time.strftime("(%Y-%m-%d %H:%M:%S)",time.localtime(time.time()+13*60*60))) f.write(content+"\n") except IOError as err: print ('File Error:'+str(err)) finally: if f: f.close() def GetLogReport(self): lastlogtime = User.GetSysinfo("LastLogGetTime") timelast = time.strptime(lastlogtime,"%Y-%m-%d %H:%M:%S") strret = "" ctn = 0 try: f=open(r"syslog.txt","r") loglist = f.readlines() for logi in loglist: timelogstr = logi[logi.index("(")+1:logi.index(")")-1] timelog = time.strptime(timelogstr,"%Y-%m-%d %H:%M:%S") if timelog>timelast: strret += logi ctn += 1 strret = strret[0:-1] f.close() User.SetSysinfo("LastLogGetTimeBak",lastlogtime) User.SetSysinfo("LastLogGetTime",time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()+13*60*60))) if ctn>0: return strret else: return "没有更新的日志信息" except Exception as e: return "日志信息获取失败:"+str(e) finally: if f: f.close() def SetLogNotReceive(self): if User.GetSysinfo("LastLogGetTime") == User.GetSysinfo("LastLogGetTimeBak"): return "日志未接受设置未执行:系统已确认接收" else : if User.SetSysinfo("LastLogGetTime",User.GetSysinfo("LastLogGetTimeBak"))=="": return "日志未接收设置失败" else: return "日志未接收设置成功" def GetCookBook(self): wcontent = "本周菜谱未上传" cbfilename = time.strftime(r"cookbook/%y-%W.txt",time.localtime()) dayofweek = (int(time.strftime("%w",time.localtime(time.time())))+6)%7 hourofday = int(time.strftime("%H",time.localtime(time.time()))) if hourofday > 10: dayofweek = (dayofweek+1)%7 hourofday = (hourofday+13)%24 print cbfilename try: f = open(cbfilename,"r") wlist=f.readlines() startline = dayofweek * 13 meal = 0 strmeal = "" if hourofday <8: startline += 1 strmeal = "今天的早饭" elif hourofday <13: startline += 5 meal = 1 strmeal = "今天的午饭" elif hourofday <19: startline += 9 meal = 2 strmeal = "今天的晚饭" else: startline += 14 strmeal = "明天的早饭" wcontent = strmeal + "是:" wcontent += "\n主食:" + self.GetDishes(wlist[startline]) wcontent += "\n副食:" + self.GetDishes(wlist[startline+1]) wcontent += "\n汤、其他:" + self.GetDishes(wlist[startline+2]) if meal==1: wcontent += "\n水果:" + self.GetDishes(wlist[startline+3]) if len(wlist)>91: wcontent += "\n本周菜谱由@" + wlist[91][wlist[91].find(":")+1:-1] + "上传" return wcontent except Exception as e: return "本周菜谱未上传" def GetDishes(self,strline): strline = strline[strline.find(":")+1:-1] dlist = strline.split("\\") dstr = "" for dish in dlist: dstr += "\n "+dish return dstr def RepairReport(self,recMsg): strret = "该功能尚未开通,敬请期待" content = recMsg.Content.strip() if not User.TestUserExist(recMsg.FromUserName): strret = "请先回复7添加姓名" elif content[1:]=="": strret = '请回复4+报备维修的物品及位置,如"4宿舍209卫生间排气扇损坏"' else: self.WriteToLog("RR:"+User.GetUserName(recMsg.FromUserName)+":"+ content[1:]) strret = "您的请求已记录,将转达至相关人员,谢谢" return strret def MedicineReport(self,recMsg): strret = "该功能尚未开通,敬请期待" content = recMsg.Content.strip() if not User.TestUserExist(recMsg.FromUserName): strret = "请先回复7添加姓名" elif content[1:]=="": strret = '请回复5+症状描述、所需的药品或想了解的问题,如"5脚扭伤需要云南白药喷雾剂"' else: self.WriteToLog("MR:"+User.GetUserName(recMsg.FromUserName)+":"+ content[1:]) strret = "您的请求已记录,将转达至相关人员,谢谢" return strret def SuggestFeedback(self,recMsg): strret = "该功能尚未开通,敬请期待" content = recMsg.Content.strip() if content[1:]=="": strret = '请回复6+意见或建议,如"6今日班车功能早日能开通"' else: if User.TestUserExist(recMsg.FromUserName): self.WriteToLog("SF:"+User.GetUserName(recMsg.FromUserName)+":"+ content[1:]) else: self.WriteToLog("SF:"+recMsg.FromUserName +":"+ content[1:]) strret = "您反映的意见、建议已记录,将转达至开发人员,谢谢" return strret def SetName(self,recMsg): strret = "" content = recMsg.Content.strip() if content[1:]=="": strret = '请输入7+姓名,例如"7王大锤"\n1、请勿添加其他非空字符\n2、多次添加将做覆盖处理\n3、回复"77"将查看系统录入的姓名"' elif content[1]=="7": strret = User.GetUserName(recMsg.FromUserName) else : content = content[1:].decode('utf-8') pattern = re.compile(u'([\u4e00-\u9fa5]+)', re.S) name = re.findall(pattern,content) if name: strret = User.SetUserName(recMsg.FromUserName,name[0].encode('utf-8')) else: strret = "设置姓名失败,请重新输入" return strret
WriteToLog对用户请求进行记录形成日志
GetLogReport将上一次查询以来的最近的日志返回,以供查询,响应请求头为logget:的消息。这个仅供管理员使用,主要是当有用户发起其他请求,通过查询日志可以获知。这里吐槽下微信公众号给个人用户提供的权限很有限,即给订阅号的权限很有限,比如用微信的菜单功能就不能使用自定义回复,订阅号只能被动回复消息,一次只能回复一条。所以才导致该程序这么设计。在写该订阅号的时候微信小程序还没开放注册,所以当时没考虑用小程序做。
SetLogNotReceive主要响应消息头为norecv:的,也是供管理员使用的,因为服务器不在国内,日志信息比较长,故而在获取日志的时候经常会收不到,而当日志请求发生后程序会标记获取日志的时间,导致再次请求仍无法获取该次标记时间前的日志,因而需要该函数取消上一次日志获取时间设置,然后就可以再次申请获取日志。当然也可以借助该函数使得每次可以查看完日志再回复状态下次可以继续查看。
GetCookBook响应消息头为2的菜谱信息查询,从上传的菜谱文件中提取下一顿的菜谱。
菜谱文件内容格式:
#Mon Main:馒头\蒸鸡蛋\大饼 Sub:八宝咸菜\素炒白菜\豆皮丝\海带丝 Soup:豆浆 Fruit: Main:米饭\馒头\蒸红薯 Sub:京酱肉丝\毛血旺\素炒冬瓜条\芹菜炒肉丝\白菜炖豆腐 Soup:紫菜蛋花汤 Fruit:苹果 Main:米饭\馒头\辣子锅盔 Sub:蒜苗回锅肉\鱼香茄子\大葱炒鸡蛋\醋溜小豆芽 Soup:黑米粥 Fruit: #Tue Main:…… …… uploader:萌萌哒强仔
按天列出,冒号前是标示,冒号后是菜谱,菜之间斜杠分隔。最后一行是上传者的昵称。文件名“17-06.txt”代表17年第6周。
GetDishes辅助实现GetCookBook功能。
RepairReport响应消息头为4的维修报备功能,由于暂时没能和后勤部门协商好,因而该功能及以下几个功能未启用,原理是当有人发起维修申请,公众号将转发消息至对应用户,这也是user.py(见下文)维护用户信息的目的之一。
MedicineReport和SuggestFeedback都是类似的方法,只是转发的对象不同,该程序都没有对这几个功能做下一步完善。
SetName用于用户设置自己的昵称,便于转发消息时供管理员识别,也使得对用户的回复称呼更友好。
Getweather.py是后来增加的一个模块,主要用于当气象采集装置因故障不可用或者想查询其他地方天气的时候,主要是通过抓取网络上的天气数据并返回。不多说,最基本的爬虫技能,抓取html页面信息。
# coding=utf-8 #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'ATP' import urllib import urllib2 import re import sys reload(sys) sys.setdefaultencoding('utf8') class weather: def __init__(self): self.root_url = 'http://www.22086.com/' def GetWeather(self,wurl='d7_sanhe'): crawurl = self.root_url+wurl+'.html' strret = "" try: request = urllib2.Request(crawurl) response = urllib2.urlopen(request) content = response.read().decode('utf-8') pattern = re.compile(ur'\u53d1\u5e03\u7684(.*?)\u4eca\u65e5',re.S) place = re.findall(pattern,content) if wurl != 'd7_sanhe': strret = place[0]+"实时天气:\n" else: strret = "--实时天气:\n" pattern = re.compile(r'hours36">' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'</div>',re.S) itemlist = re.findall(pattern,content) for i in range(0,15): strret += itemlist[0][i]+'\n' updatetime = itemlist[0][15] pattern = re.compile(r'(hours36kq">)',re.S) something = re.findall(pattern,content) if something: pattern = re.compile(r'hours36kq">' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'<li>(.*?)</li><li>(.*?)</li><li>(.*?)</li><li>(.*?)</li>' r'</div>',re.S) itemlist = re.findall(pattern,content) for item in itemlist[0]: strret += item + '\n' strret += updatetime except Exception,e: print e return strretuser.py该模块最初用于记录用户信息,主要是完成后台的简单统计功能,比如我作为管理者不需要登陆微信订阅号后台就可以查看最近大家的请求情况等。通过读写文本文件维护一个字典,实现对用户id和用户名之间的解析查询。后来干脆把天气信息和一些系统信息都用类似方法维护,所以又增加了一个文本文件用来存储系统信息字典,一个文件用来存储气象数据。
# -*- coding: utf-8 -*- # filename: user.py import json import time class User(object): userdic = {} sysdic = {} weatherdic = {} @staticmethod def __init__(): with open("user.txt", 'rt') as userinfo: User.userdic = json.load(userinfo) with open("sysinfo.txt", 'rt') as sysinfo: User.sysdic = json.load(sysinfo) with open("weather.txt", 'rt') as weatherinfo: User.weatherdic = json.load(weatherinfo) @staticmethod def SetUserName(openid,name): ret = "" if User.userdic.has_key(openid): User.userdic[openid]=name ret = "名字更改成功" else: User.userdic[openid]=name ret = "名字添加成功" with open("user.txt", 'wt') as userinfo: userinfo.write(json.dumps(User.userdic)) return ret @staticmethod def GetUserName(openid): ret = "" if User.userdic.has_key(openid): return User.userdic[openid] else: return openid @staticmethod def TestUserExist(openid): if User.userdic.has_key(openid): return True else: return False @staticmethod def GetWeather(): wcontent = "数据未采集" setok = False try: wcontent = "今日--天气:\n" wcontent += "温度: %s ℃\n" % User.weatherdic["temperature"] wcontent += "湿度:%s %%\n" % User.weatherdic["humidity"] wcontent += "大气压强:%s kPa\n" % User.weatherdic["pressure"] wcontent += "PM2.5: %s μg/㎥\n" % User.weatherdic["PM2.5"] wcontent += "PM10: %s μg/㎥\n" % User.weatherdic["PM10"] #wcontent += "PM0.3~2.5: %s μg/㎥\n" % User.weatherdic["PM0.3~2.5"] #wcontent += "PM2.5~10: %s μg/㎥\n" % User.weatherdic["PM2.5~10"] wcontent += "数据更新于 %s " % User.weatherdic["update"] setok = True finally: if not setok: wcontent = "数据未采集" return wcontent @staticmethod def SetWeather(recTxt): ret = "天气数据设置失败" content = recTxt[5:] contlist = content.split(";") if len(contlist)==5: User.weatherdic["temperature"] = contlist[0] User.weatherdic["humidity"] =contlist[1] User.weatherdic["pressure"] =contlist[2] User.weatherdic["PM2.5"] =contlist[3] User.weatherdic["PM10"] =contlist[4] #User.weatherdic["PM0.3~2.5"] =contlist[5] #User.weatherdic["PM2.5~10"] =contlist[6] User.weatherdic["update"] = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()+13*60*60)) with open("weather.txt", 'wt') as weatherinfo: weatherinfo.write(json.dumps(User.weatherdic)) ret = "天气数据设置成功" return ret @staticmethod def GetSysinfo(key): if User.sysdic.has_key(key): return User.sysdic[key] else: return "" @staticmethod def SetSysinfo(key,value): ret = "" User.sysdic[key] = value with open("sysinfo.txt",'wt') as sysinfo: sysinfo.write(json.dumps(User.sysdic)) ret = "系统信息设置成功" return ret
构造函数初始化打开三个文件。
SetUserName设置用户ID及对应昵称。
GetUserName根据用户ID返回用户昵称。
TestUserExist测试用户ID是否已存在,从而在程序处理上进行区别。
GetWeather从气象采集器上传的信息中获取气象情况,该函数供查询马道天气时使用,当最近周期内气象采集装置或人工未更新气象信息或查询其他地区天气信息,则需要从网上抓取,即weather类的getweather方法。
SetWeather响应请求头为wset:的消息,即手动设置气象信息。主要是当气象采集装置失效时,手动上传气象信息。
GetSysInfo和SetSysInfo通过对维护的字典进行读写实现键值对形式的系统信息存储读取。
AutoWeatherServer.py就是气象信息采集装置对应的服务端,通过监听端口对传入的气象信息进行记录,仅此而已。
import socket import threading import time from user import User def tcplink(sock,addr): strlog = 'Accept new connection from %s:%s...' % addr WriteToLog(strlog) sock.send('begin') try: while True: data = sock.recv(1024) time.sleep(1) if data: strlog = "recv from %s:%s :" % addr strlog += data WriteToLog(strlog) if data.startswith("wset:"): ret = User.SetWeather(data) print ret elif data == 'exit' or not data: break sock.send('Recved') sock.close() strlog = 'Connection from %s:%s closed.' % addr WriteToLog(strlog) except Exception as e: print 'Error occur ' + str(e) def WriteToLog(content): try: f = open(r"weatherlog.txt", "a") f.write(time.strftime("(%Y-%m-%d %H:%M:%S)", time.localtime(time.time() + 13 * 60 * 60))) f.write(content + "\n") except IOError as err: print ('File Error:' + str(err)) finally: if f: f.close() if __name__ == '__main__': s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('123.123.123.123', 9999)) s.listen(5) print 'Waiting for connection' while True: sock, addr = s.accept() t = threading.Thread(target=tcplink, args=(sock, addr)) t.start()
tcplink处理气象采集装置连接请求并记录。
writeToLog记录日志信息
图1 公众号运行截图
图2 公众号运行截图
整个下来涉及的程序并不多也不难,只是作为初学python的练手,顺便实现了点小功能方便生活,所以里面还有很多不完善的地方,距离实用还有些距离,比如性能、容错性、可用性、安全性等等。仅供大家参考。
【附录】:
代码及相关资料下载:http://download.csdn.net/download/atp1992/10209702
原博文:http://www.straka.cn/blog/wechat_subscribe_python_server/