上一節(jié)恭垦,我在一個(gè)Django項(xiàng)目中集成了 基于WeRoBot的微信公眾號(hào)后臺(tái)番挺,成功與服務(wù)器完成了對(duì)接玄柏,并且可以對(duì)用戶的任意消息做出響應(yīng)(回復(fù)一個(gè)“hello”)瀑晒,簡(jiǎn)單來說苔悦,就是搭建起了一個(gè)開發(fā)框架间坐。
這一節(jié)中,我將繼續(xù)用 WeRoBot 在這個(gè)開發(fā)框架上擴(kuò)展一些功能地技,讓公眾號(hào)的交互豐富起來,思來想去作谚,我挑了三個(gè)相對(duì)簡(jiǎn)單的功能進(jìn)行實(shí)現(xiàn):簡(jiǎn)單的聊天功能妹懒,天氣查詢,講笑話匾竿。
下面是實(shí)現(xiàn)這三個(gè)功能的過程和心得記錄岭妖,分享給大家假夺。
簡(jiǎn)單的聊天機(jī)器人
原理:文本匹配
WeRoBot 也有微信機(jī)器人的意思,你可以使用它定制公眾號(hào)的響應(yīng)蜻韭。
WeRoBot中有這么一個(gè)消息處理函數(shù)
@robot.text
def echo(message):
return message.content
他只能響應(yīng)用戶發(fā)送過來的 文本消息闺魏。
文本消息存儲(chǔ)在參數(shù) message 變量中析桥,我們可以通過 msg = message.content
取出用戶發(fā)過來的消息的文本泡仗。
(詳情見官方文檔:WeRoBot官方文檔)
而通過對(duì)消息文本msg
進(jìn)行匹配娩怎,我們可以為公眾號(hào)加入簡(jiǎn)單的聊天功能。
機(jī)器人代碼如下:
@robot.text
def 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我是囈語的管家機(jī)器人崩瓤,主人還沒給我起名字 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 '謝謝夸獎(jiǎng) (??????)??'
elif re.compile(".*?傻逼.*?").match(msg):
return '爸爸不想理你'
except Exception as e:
print e
運(yùn)行效果:
“天氣查詢” 功能實(shí)現(xiàn)
原理:封裝城市解析接口,調(diào)用百度天氣API
要實(shí)現(xiàn)天氣查詢功能其實(shí)比較簡(jiǎn)單卖鲤,網(wǎng)上不少現(xiàn)成的API肾扰,不過到底選哪一家的API是個(gè)值得商榷的問題,因?yàn)槲覀冞x取的API最好要 穩(wěn)定蛋逾、信息豐富集晚、免費(fèi)、準(zhǔn)確区匣。
- 接口的穩(wěn)定性毋庸置疑偷拔,如果用著用著接口提供方突然不提供服務(wù)蛤签,你還要臨時(shí)換接口戳晌,維護(hù)成本高搔驼。
- 使用的天氣接口最好能提供比較豐富的天氣信息,比如最高溫荒揣、最低溫嘉蕾、實(shí)時(shí)溫度儿普、未來X天的溫度、PM2.5值、風(fēng)力風(fēng)向、天氣情況等等,這樣展示給用戶的信息會(huì)比較全面。
- 免費(fèi)更不用說隔缀,剛開始維護(hù)公眾號(hào)能找到免費(fèi)的優(yōu)質(zhì)服務(wù)就不掏腰包,畢竟夠用就行渊鞋。
- 接口提供的天氣信息一定要準(zhǔn)確癌刽,如果信息不準(zhǔn)確,這個(gè)服務(wù)也就沒有了價(jià)值盹兢。
秉承著以上原則篩選網(wǎng)上的天氣API剂娄,下面分享幾個(gè)我嘗試過的:
- 中國(guó)天氣網(wǎng)的API是中國(guó)氣象局提供的庭砍,應(yīng)該是最官方的搔体,但是用起來有各種問題,提供的天氣信息也不夠豐富蛾娶,不推薦使用。
- 心知天氣 專業(yè)提供天氣服務(wù)的公司,網(wǎng)站界面高大上揪惦,文檔詳細(xì)媳荒,服務(wù)穩(wěn)定,使用舒服自晰,唯一的缺點(diǎn)是免費(fèi)版只提供全國(guó)地級(jí)市的天氣查詢酬荞,像我們這兒一個(gè)小小縣城就沒法查詢 _(:3」∠)_凿傅。
- 百度天氣API被辑,目前在使用的触机,以上條件基本都符合,穩(wěn)定性待觀察壶硅,剛開始用威兜。需要申請(qǐng)AccessKey,我直接用了一個(gè)博客上分享的(因?yàn)樽约旱腶k使用有問題庐椒,無法正確請(qǐng)求到天氣信息椒舵,論壇上很多人反映這個(gè)問題),博客鏈接見:百度天氣接口api - 簡(jiǎn)書约谈。【來自后期的提示:該AK已過期】
順便貼上知乎上關(guān)于天氣API的討論:網(wǎng)上的天氣 API 哪一個(gè)更加可靠笔宿?
我封裝了“解析消息中包含的城市”接口,由于代碼太長(zhǎng)棱诱,就不在這里帖出來了泼橘,感興趣的,可以在瀏覽器中輸入這段url 或者直接點(diǎn)擊嘗試:
它會(huì)返回msg參數(shù)中包含的所有城市名稱鲤脏。
我將它集成到了天氣查詢功能中们颜,使其顯得更加智能。
下面是天氣查詢的代碼:
#coding=utf8
from werobot import WeRoBot
from werobot.replies import WeChatReply, TextReply, ImageReply, MusicReply
import re
import urllib,urllib2
import logging
import json
timeout=30 # 超時(shí)時(shí)間
bdkey = 'FK9mkfdQsloEngodbFl4FeY3' # 百度天氣ak
def 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 citys
def get_weather(city):
# 獲取天氣數(shù)據(jù)
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.text
def 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:] # 未來幾天的預(yù)報(bào)
res_msg += '【%s】\n? 今天 %s\n當(dāng)日溫度:%s\n天氣情況:%s\n風(fēng)向風(fēng)力:%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當(dāng)日溫度:%s\n天氣情況:%s\n風(fēng)向風(fēng)力:%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
運(yùn)行效果:
“講個(gè)段子” 功能實(shí)現(xiàn)
“講個(gè)段子”應(yīng)用場(chǎng)景是這樣的:在你煩心的時(shí)候,在公眾號(hào)中輸入“xxx硫嘶,講個(gè)段子”阻问,它便會(huì)機(jī)智的回復(fù)一個(gè)段子給你,讓你在愁眉緊鎖之余也能輕松一下沦疾。
實(shí)現(xiàn)這個(gè)功能大體需要兩個(gè)步驟:接口實(shí)現(xiàn) 和 微信后臺(tái)調(diào)用邏輯称近。
和天氣查詢不同,我打算自己實(shí)現(xiàn)這個(gè)接口哮塞,原理是編寫一個(gè)腳本定期抓取糗事百科上的段子刨秆,存儲(chǔ)在服務(wù)器的數(shù)據(jù)庫中,再編寫接口函數(shù)忆畅,用于從數(shù)據(jù)庫中隨機(jī)抽取段子并返回衡未。
以下是實(shí)現(xiàn)這個(gè)功能的步驟:
1、創(chuàng)建數(shù)據(jù)庫表
作用:在數(shù)據(jù)庫(mysql)中創(chuàng)建用于存儲(chǔ)段子的表
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;
2、編寫段子抓取腳本
作用:抓取糗事百科官網(wǎng)“文本”欄目下所有頁面的段子缓醋,存儲(chǔ)在數(shù)據(jù)庫中(只存儲(chǔ)之前沒出現(xiàn)過的段子剔交,避免重復(fù))。
#coding=utf-8
import urllib
import urllib2
import re
import MySQLdb
import time
timeout=5 # 超時(shí)時(shí)間
host = 'http://www.qiushibaike.com' # 糗事百科主頁面
target = 'text' # 糗事百科的"文字"欄目
min_laugh_num = 500 # 好笑數(shù)低于該值改衩,不保留
min_joke_num = 10 # 一次性最少抓到的笑話個(gè)數(shù)
# 數(shù)據(jù)庫設(shè)置
username = 'YOUR_USERNAME' # 你的數(shù)據(jù)庫用戶名
password = 'YOUR_PASSWORD' # 你的數(shù)據(jù)庫密碼
dbname = 'YOUR_DB' # 你創(chuàng)建的表所在的數(shù)據(jù)庫
dbport = 3306
def 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 錯(cuò)誤,程序休眠了一下……'
except Exception,e:
raise Exception,'[Error] get_html()獲取源碼失敗\n%s'%e
return response.read()
def getPagesum():
# 獲取總頁數(shù)
try:
url = '%s/%s'%(host,target)
html = get_html(url,timeout)
pattern = re.compile(r'<span class="page-numbers">(.*?)</span>.*?</a>.*?</li>',re.S)
items = re.findall(pattern,html)
except Exception,e:
raise Exception,'[Error] getPagesum()獲取總頁數(shù)失敗\n%s'%e
return int(items[-1])
def connectMySQL():
# 連接mysql數(shù)據(jù)庫
conn = MySQLdb.connect(
host='localhost',
port=dbport,
user=username,
passwd=password,
db=dbname,
charset='utf8',
)
return conn
def getJokes():
# 抓取糗事百科的段子
# 獲取總頁數(shù)
print '開始抓取……'
try:
pagesum = getPagesum()
except Exception,e:
print e
return []
joke_list = []
# 開始爬取
for page in range(1,pagesum+1):
print '當(dāng)前頁數(shù):',page
url = '%s/%s/page/%d/'%(host,target,page)
try:
html = get_html(url,timeout)
except Exception,e:
print e
print '抓取出錯(cuò)驯镊,跳過第%s頁'%page
continue
print '正在匹配……'
pattern = re.compile('<div.*?author clearfix">.*?<h2>(.*?)</h2>.*?<div class="content">.*?<span>(.*?)</span>.*?</div>(.*?)<i class="number">(.*?)</i>.*?<i class="number">(.*?)</i>',re.S)
items = re.findall(pattern,html)
# 匹配到段子
for item in items:
joke = {}
joke['username'] = item[0].strip()
joke['content'] = item[1].strip().replace('<br/>','\n').replace('"','\"')
joke['imgsrc'] = re.findall('<img.*?src="(.*?)".*?',item[2])
if len(joke['imgsrc'])==0:
joke['imgsrc'] = ''
joke['laugh_num']= int(item[3].strip())
joke['comment_num'] = int(item[4].strip())
info = '用戶名:%s\n內(nèi)容:\n%s\n圖片地址:%s\n好笑數(shù):%d\n評(píng)論數(shù):%d\n'%\
( joke['username'] , joke['content'] , joke['imgsrc'] , joke['laugh_num'] , joke['comment_num'] )
if joke['imgsrc']=='' and joke['laugh_num'] > min_laugh_num:
print len(joke_list)
print info
joke_list.append(joke)
return joke_list
def save2mysql(joke_list):
# 將抓取的段子存入數(shù)據(jù)庫
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 '-> 該段子已存在于數(shù)據(jù)庫葫督!放棄插入!'
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 e
if __name__=='__main__':
main()
運(yùn)行效果:
3板惑、cron定期運(yùn)行腳本
作用:定期抓取優(yōu)質(zhì)段子橄镜,存入數(shù)據(jù)庫
步驟:
在命令行中運(yùn)行
crontab -e
在編輯頁面加入新的計(jì)劃任務(wù)
*/10 * * * * /root/workspace/BLOG_VENV/bin/python /root/workspace/crawler/craw_jokes.py
這里解釋一下上面這條命令的意思:
***/10 * * * *** ----> 代表每10分鐘執(zhí)行一次后面的命令。五個(gè)“*”分別代表“分 時(shí) 日 月 周”冯乘,如果在第一個(gè)“*”后面加上除號(hào)/和一個(gè)數(shù)字N洽胶,代表每N分鐘執(zhí)行一次后面的命令。以上是crontab計(jì)劃任務(wù)時(shí)間控制的格式裆馒。
/root/workspace/BLOG_VENV/bin/python ----> 這個(gè)是我的python解釋器的地址
/root/workspace/crawler/craw_jokes.py ----> 這是我們上一節(jié)所寫的腳本的存放地址
以上命令合起來就是:每十分鐘抓取糗事百科姊氓,將優(yōu)質(zhì)段子存入數(shù)據(jù)庫。
好了喷好,段子我們有了翔横,接下來我們只要寫一個(gè)接口函數(shù),每次調(diào)用便從數(shù)據(jù)庫中隨機(jī)取出一條段子梗搅,并以json的格式返回(json是一種表示和存儲(chǔ)數(shù)據(jù)的形式禾唁,和字典類似)。
4无切、接口函數(shù)實(shí)現(xiàn)
作用:每次調(diào)用荡短,從數(shù)據(jù)庫中隨機(jī)取出一條段子,以json的格式返回哆键。
前言:代碼集成在django中掘托,不想在django中使用的可以適當(dāng)修改代碼。
代碼:
from django.http import HttpResponse
import MySQLdb
import random
import json
# 公共部分
# 數(shù)據(jù)庫設(shè)置
username = 'YOUR_USERNAME' # 你的數(shù)據(jù)庫用戶名
password = 'YOUR_PASSWORD' # 你的數(shù)據(jù)庫密碼
dbname = 'YOUR_DB' # 你創(chuàng)建的表所在的數(shù)據(jù)庫
dbport = 3306
# 數(shù)據(jù)庫連接函數(shù)
def connectMySQL():
# 連接mysql數(shù)據(jù)庫
conn = MySQLdb.connect(
host='localhost',
port=dbport,
user=username,
passwd=password,
db=dbname,
charset='utf8',
)
return conn
# 接口部分
# 返回一條糗事百科上的段子
def get_joke(request):
response = ''
try:
# 連接數(shù)據(jù)庫
conn = connectMySQL()
cur = conn.cursor()
# 生成隨機(jī)抓取id
sql = 'select count(*) from qiushi_joke'
cur.execute(sql)
joke_sum = cur.fetchone()[0]
joke_idx = random.randint(1,joke_sum)
# 抓取該id的段子數(shù)據(jù)
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)
# 關(guān)閉數(shù)據(jù)庫連接
cur.close()
conn.close()
except Exception as e:
print e
return HttpResponse(response)
前端接口封裝好之后洼哎,可以在瀏覽器中輸入以下url測(cè)試這個(gè)接口:
http://www.yangyingming.com/api/get_joke/
每次刷新都會(huì)返回不同的段子烫映。
5、集成在微信機(jī)器人中
作用:將“講個(gè)段子”功能集成到微信機(jī)器人的聊天功能中噩峦,用戶在聊天窗口發(fā)送“講個(gè)段子”類似的消息時(shí)锭沟,隨機(jī)回復(fù)一條段子。
代碼:
@robot.text
def 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搞笑指數(shù):%d'%(joke['content'].encode('utf8'),joke['laugh_num'])
運(yùn)行效果:
以上识补,就是我的微信公眾號(hào)開發(fā)中 簡(jiǎn)單的聊天功能族淮,天氣查詢,講笑話 三個(gè)功能的實(shí)現(xiàn)過程,在這里分享給大家祝辣,歡迎吐槽贴妻!
后續(xù)
基于Django、WeRoBot的微信公眾平臺(tái)開發(fā)(二) - 后續(xù)