本文共 11491 字,大约阅读时间需要 38 分钟。
上一节的中,我在一个Django项目中集成了 基于WeRoBot的微信公众号后台,成功与服务器完成了对接,并且可以对用户的任意消息做出响应(回复一个“hello”),简单来说,就是搭建起了一个开发框架。
这一节中,我将继续用 WeRoBot 在这个开发框架上扩展一些功能,让公众号的交互丰富起来,思来想去,我挑了三个相对简单的功能进行实现:简单的聊天功能,天气查询,讲笑话。
下面是实现这三个功能的过程和心得记录,分享给大家。
原理:文本匹配
WeRoBot 也有微信机器人的意思,你可以使用它定制公众号的响应。
WeRoBot中有这么一个消息处理函数
@robot.textdef echo(message): return message.content
他只能响应用户发送过来的 文本消息。
文本消息存储在参数 message 变量中,我们可以通过 msg = message.content
取出用户发过来的消息的文本。
(详情见官方文档:)
而通过对消息文本msg
进行匹配,我们可以为公众号加入简单的聊天功能。
机器人代码如下:
@robot.textdef echo(message): try: # 提取消息 msg = message.content.strip().lower().encode('utf8') # 解析消息 if re.compile(".*?你好.*?").match(msg) or\ re.compile(".*?嗨.*?").match(msg) or\ re.compile(".*?哈喽.*?").match(msg) or\ re.compile(".*?hello.*?").match(msg) or\ re.compile(".*?hi.*?").match(msg) or\ re.compile(".*?who are you.*?").match(msg) or\ re.compile(".*?你是谁.*?").match(msg) or\ re.compile(".*?你的名字.*?").match(msg) or\ re.compile(".*?什么名字.*?").match(msg) : return "你好~\n我是呓语的管家机器人,主人还没给我起名字 T_T\n有什么能帮您的吗?(绅士脸)" elif re.compile(".*?厉害.*?").match(msg): return '承让承让 (๑•̀ㅂ•́)ﻭ✧' elif re.compile(".*?想你.*?").match(msg): return '我也想你' elif re.compile(".*?miss you.*?").match(msg): return 'I miss you,too' elif re.compile(".*?我爱你.*?").match(msg): return '我也爱你' elif re.compile(".*?love you.*?").match(msg): return 'I love you,too' elif re.compile(".*?美女.*?").match(msg): return '我是男生哦♂' elif re.compile(".*?帅哥.*?").match(msg): return '谢谢夸奖 (๑•̀ㅂ•́)ﻭ✧' elif re.compile(".*?傻逼.*?").match(msg): return '爸爸不想理你' except Exception as e: print e
运行效果:
原理:封装城市解析接口,调用百度天气API
要实现天气查询功能其实比较简单,网上不少现成的API,不过到底选哪一家的API是个值得商榷的问题,因为我们选取的API最好要 稳定、信息丰富、免费、准确。
秉承着以上原则筛选网上的天气API,下面分享几个我尝试过的:
顺便贴上知乎上关于天气API的讨论:
我封装了“解析消息中包含的城市”接口,由于代码太长,就不在这里帖出来了,感兴趣的,可以在浏览器中输入这段url 或者直接点击尝试:
它会返回msg参数中包含的所有城市名称。
我将它集成到了天气查询功能中,使其显得更加智能。
下面是天气查询的代码:
#coding=utf8from werobot import WeRoBotfrom werobot.replies import WeChatReply, TextReply, ImageReply, MusicReplyimport reimport urllib,urllib2import loggingimport jsontimeout=30 # 超时时间bdkey = 'FK9mkfdQsloEngodbFl4FeY3' # 百度天气akdef get_citys_in_msg(msg): # 获取消息中包含的城市 api_url = 'http://www.yangyingming.com/api/parse_city?%s'%(urllib.urlencode({'msg':msg})) citys = get_html(api_url,timeout=timeout) return citysdef get_weather(city): # 获取天气数据 url = 'http://api.map.baidu.com/telematics/v3/weather' param = urllib.urlencode({ 'location':city, 'ak':bdkey, 'output':'json', }) api_url = '%s?%s'%(url,param) wdata = get_html(api_url,timeout=timeout) return wdata@robot.textdef echo(message): if re.compile(".*?天气.*?").match(msg): res_msg = '' # 取出该消息中包含的所有城市 citys = get_citys_in_msg(msg).split(',') # 获得每一座城市的天气状况 if citys[0]=='': return '亲爱的,你想知道哪座城市的天气呢?' else: for city in citys: if res_msg!='': res_msg += '\n\n' wdata = get_weather(city) wdata = json.loads(wdata) if wdata['status']=='success': index = wdata['results'][0]['index'] pm25 = wdata['results'][0]['pm25'] w = wdata['results'][0]['weather_data'] today = w[0] future = w[1:] # 未来几天的预报 res_msg += '【%s】\n☀ 今天 %s\n当日温度:%s\n天气情况:%s\n风向风力:%s\nPM2.5:%s'%\ (city,today['date'].encode('utf8'),today['temperature'].encode('utf8'),today['weather'].encode('utf8'),today['wind'].encode('utf8'),pm25.encode('utf8')) for today in future: res_msg += '\n☀ %s\n当日温度:%s\n天气情况:%s\n风向风力:%s'%\ (today['date'].encode('utf8'),today['temperature'].encode('utf8'),today['weather'].encode('utf8'),today['wind'].encode('utf8')) res_msg += '\n【温馨提示】' for i in index: res_msg += '\n❤ %s:%s\n%s'%\ (i['tipt'].encode('utf8'),i['zs'].encode('utf8'),i['des'].encode('utf8')) else: res_msg += '没有找到%s的天气信息'%city return res_msg
运行效果:
“讲个段子”应用场景是这样的:在你烦心的时候,在公众号中输入“xxx,讲个段子”,它便会机智的回复一个段子给你,让你在愁眉紧锁之余也能轻松一下。
实现这个功能大体需要两个步骤:接口实现 和 微信后台调用逻辑。
和天气查询不同,我打算自己实现这个接口,原理是编写一个脚本定期抓取糗事百科上的段子,存储在服务器的数据库中,再编写接口函数,用于从数据库中随机抽取段子并返回。
以下是实现这个功能的步骤:
作用:在数据库(mysql)中创建用于存储段子的表
create table `qiushi_joke` ( `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `username` varchar(128) NOT NULL, `content` varchar(1024) NOT NULL, `laugh_num` int(11) NOT NULL, `comment_num` int(11) NOT NULL, `imgurl` varchar(255) DEFAULT NULL) DEFAULT CHARSET=utf8;
作用:抓取糗事百科官网“文本”栏目下所有页面的段子,存储在数据库中(只存储之前没出现过的段子,避免重复)。
#coding=utf-8import urllibimport urllib2import reimport MySQLdbimport timetimeout=5 # 超时时间host = 'http://www.qiushibaike.com' # 糗事百科主页面target = 'text' # 糗事百科的"文字"栏目min_laugh_num = 500 # 好笑数低于该值,不保留min_joke_num = 10 # 一次性最少抓到的笑话个数# 数据库设置username = 'YOUR_USERNAME' # 你的数据库用户名password = 'YOUR_PASSWORD' # 你的数据库密码dbname = 'YOUR_DB' # 你创建的表所在的数据库dbport = 3306def get_html(url,timeout=None): # 获取指定url的html源码 try: headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6' } request = urllib2.Request(url,headers=headers) response = urllib2.urlopen(request,timeout=timeout) except urllib2.HTTPError,e: time.sleep(2) print e raise Exception,'[Error] 遇到HTTP 503 错误,程序休眠了一下……' except Exception,e: raise Exception,'[Error] get_html()获取源码失败\n%s'%e return response.read()def getPagesum(): # 获取总页数 try: url = '%s/%s'%(host,target) html = get_html(url,timeout) pattern = re.compile(r'(.*?).*?.*?',re.S) items = re.findall(pattern,html) except Exception,e: raise Exception,'[Error] getPagesum()获取总页数失败\n%s'%e return int(items[-1])def connectMySQL(): # 连接mysql数据库 conn = MySQLdb.connect( host='localhost', port=dbport, user=username, passwd=password, db=dbname, charset='utf8', ) return conndef getJokes(): # 抓取糗事百科的段子 # 获取总页数 print '开始抓取……' try: pagesum = getPagesum() except Exception,e: print e return [] joke_list = [] # 开始爬取 for page in range(1,pagesum+1): print '当前页数:',page url = '%s/%s/page/%d/'%(host,target,page) try: html = get_html(url,timeout) except Exception,e: print e print '抓取出错,跳过第%s页'%page continue print '正在匹配……' pattern = re.compile('.*? (.*?)
.*?.*? (.*?).*?(.*?) (.*?).*? (.*?)',re.S) items = re.findall(pattern,html) # 匹配到段子 for item in items: joke = {} joke['username'] = item[0].strip() joke['content'] = item[1].strip().replace(' ','\n').replace('"','\"') joke['imgsrc'] = re.findall('min_laugh_num: print len(joke_list) print info joke_list.append(joke) return joke_listdef save2mysql(joke_list): # 将抓取的段子存入数据库 conn = connectMySQL() cur = conn.cursor() for i,joke in enumerate(joke_list): print '正在插入第%d条段子……'%(i+1) sql = 'select 1 from qiushi_joke where content = "%s" limit 1; '%(joke['content']) isExist = cur.execute(sql) if isExist==1: print '-> 该段子已存在于数据库!放弃插入!' else: sql = 'insert into qiushi_joke (`username`,`content`,`laugh_num`,`comment_num`,`imgurl`) values ("%s","%s","%d","%d","%s")'%\ ( joke['username'] , joke['content'] , joke['laugh_num'] , joke['comment_num'] , joke['imgsrc'] ) cur.execute(sql) print '正在提交以上所有操作……' conn.commit() cur.close() conn.close()def main(): # 主程序 try: while True: joke_list = getJokes() if len(joke_list)>=min_joke_num: # 抓取到至少min_joke_num条笑话才行 break time.sleep(2) save2mysql(joke_list) except Exception,e: print eif __name__=='__main__': main()
运行效果:
作用:定期抓取优质段子,存入数据库
步骤:
在命令行中运行
crontab -e
在编辑页面加入新的计划任务
*/10 * * * * /root/workspace/BLOG_VENV/bin/python /root/workspace/crawler/craw_jokes.py
这里解释一下上面这条命令的意思:
*/10 * * * * ----> 代表每10分钟执行一次后面的命令。五个“*”分别代表“分 时 日 月 周”,如果在第一个“*”后面加上除号/和一个数字N,代表每N分钟执行一次后面的命令。以上是crontab计划任务时间控制的格式。
/root/workspace/BLOG_VENV/bin/python ----> 这个是我的python解释器的地址
/root/workspace/crawler/craw_jokes.py ----> 这是我们上一节所写的脚本的存放地址
以上命令合起来就是:每十分钟抓取糗事百科,将优质段子存入数据库。
好了,段子我们有了,接下来我们只要写一个接口函数,每次调用便从数据库中随机取出一条段子,并以json的格式返回(json是一种表示和存储数据的形式,和字典类似)。
作用:每次调用,从数据库中随机取出一条段子,以json的格式返回。
前言:代码集成在django中,不想在django中使用的可以适当修改代码。
代码:
from django.http import HttpResponseimport MySQLdbimport randomimport json# 公共部分# 数据库设置username = 'YOUR_USERNAME' # 你的数据库用户名password = 'YOUR_PASSWORD' # 你的数据库密码dbname = 'YOUR_DB' # 你创建的表所在的数据库dbport = 3306# 数据库连接函数def connectMySQL(): # 连接mysql数据库 conn = MySQLdb.connect( host='localhost', port=dbport, user=username, passwd=password, db=dbname, charset='utf8', ) return conn# 接口部分# 返回一条糗事百科上的段子def get_joke(request): response = '' try: # 连接数据库 conn = connectMySQL() cur = conn.cursor() # 生成随机抓取id sql = 'select count(*) from qiushi_joke' cur.execute(sql) joke_sum = cur.fetchone()[0] joke_idx = random.randint(1,joke_sum) # 抓取该id的段子数据 sql = 'select * from qiushi_joke where id=%d'%joke_idx cur.execute(sql) joke = {} joke['id'],joke['username'],joke['content'],joke['laugh_num'],joke['comment_num'],joke['imgurl'] = cur.fetchone() response = json.dumps(joke,ensure_ascii=False) # 关闭数据库连接 cur.close() conn.close() except Exception as e: print e return HttpResponse(response)
前端接口封装好之后,可以在浏览器中输入以下url测试这个接口:
每次刷新都会返回不同的段子。
作用:将“讲个段子”功能集成到微信机器人的聊天功能中,用户在聊天窗口发送“讲个段子”类似的消息时,随机回复一条段子。
代码:
@robot.textdef echo(message): if re.compile(".*?笑话.*?").match(msg) or\ re.compile(".*?段子.*?").match(msg): apiurl = "http://www.yangyingming.com/api/get_joke" response = get_html(apiurl,timeout=timeout) joke = json.loads(response) return '%s\n搞笑指数:%d'%(joke['content'].encode('utf8'),joke['laugh_num'])
运行效果:
以上,就是我的微信公众号开发中 简单的聊天功能,天气查询,讲笑话 三个功能的实现过程,在这里分享给大家,欢迎吐槽!
推荐链接:
转载地址:http://czwsi.baihongyu.com/