[TOC]
一、概述
上一篇文章python-定時(shí)爬取指定城市天氣(一)-發(fā)送給關(guān)心的微信好友中我們講述了怎么定時(shí)爬取城市天氣折晦,并發(fā)送給指定微信好友,文末遺留兩個(gè)問(wèn)題
- 定時(shí)任務(wù)做成windows服務(wù),這樣更優(yōu)雅在抛,隨開(kāi)機(jī)啟動(dòng)
- 發(fā)送消息給微信好友換成發(fā)送郵件給指定郵箱
本篇文章我們?cè)谠瓉?lái)代碼的基礎(chǔ)上進(jìn)行了一定的模塊拆分,并處理以上兩個(gè)問(wèn)題
二萧恕、模塊重新劃分
- 新增my_job.py文件刚梭,把任務(wù)模塊單獨(dú)劃分出來(lái)
之前的定時(shí)任務(wù)使用的是apscheduler庫(kù)做的,并且任務(wù)類在main函數(shù)所在py文件中票唆,這樣導(dǎo)致主py文件很難進(jìn)行修改
- 新增util.py文件
包含公用的方法朴读,比如目前的字典轉(zhuǎn)字符串
- 新增weather_service.py文件
主要負(fù)責(zé)構(gòu)造windows服務(wù),也是一個(gè)主py文件走趋,不同于第一篇文章的主py文件weath_report.py衅金,這是我們實(shí)現(xiàn)的兩種定時(shí)任務(wù),可分別運(yùn)行簿煌,如果想把天氣信息通知微信好友則啟動(dòng)weath_report.py典挑,可參考文章ython-定時(shí)爬取指定城市天氣(一)-發(fā)送給關(guān)心的微信好友,如果是通過(guò)發(fā)送郵件的方式則直接把weather_service.py安裝成windows服務(wù)啦吧,并啟動(dòng)即可您觉,記住需要配置運(yùn)行的任務(wù)列表,下邊會(huì)講述怎么配置任務(wù)
- 新增timing_task.py文件
包含任務(wù)方法executeJob()授滓,主要是在服務(wù)中循環(huán)跑琳水,然后在合適的時(shí)間爬取天氣并發(fā)送到指定郵箱肆糕,任務(wù)的參數(shù)是通過(guò)配置json串來(lái)實(shí)現(xiàn)
三、優(yōu)化定時(shí)任務(wù)
本篇文章的定時(shí)任務(wù)是運(yùn)行在windows服務(wù)中的在孝,因此我們首先需要安裝pywin32模塊
- 安裝pywin32
pip install pywin32
- 服務(wù)操作相關(guān)命令
1.安裝服務(wù) python PythonService.py install
2.讓服務(wù)自動(dòng)啟動(dòng) python PythonService.py --startup auto install
3.啟動(dòng)服務(wù) python PythonService.py start
4.重啟服務(wù) python PythonService.py restart
5.停止服務(wù) python PythonService.py stop
6.刪除/卸載服務(wù) python PythonService.py remove
- 啟動(dòng)服務(wù)時(shí)被拒絕
Installing service timingTaskDaemon
Error installing service: 拒絕訪問(wèn)诚啃。 (5)
a.大多數(shù)原因是由于python環(huán)境配置的問(wèn)題,python默認(rèn)安裝時(shí)配置的pah是用戶環(huán)境變量私沮,這里我們需要改成系統(tǒng)環(huán)境變量始赎,具體可以參考Python 寫(xiě)windows service 及 start service 出現(xiàn)錯(cuò)誤 1053:服務(wù)沒(méi)有及時(shí)響應(yīng)啟動(dòng)或控制請(qǐng)求
b.考慮命令行是否有權(quán)限,我自己的win8系統(tǒng)默認(rèn)權(quán)限就不夠仔燕,需要右鍵管理員啟動(dòng)才可以
- 實(shí)現(xiàn)windows服務(wù)功能造垛,我們需要繼承win32serviceutil.ServiceFramework這個(gè)類,把需要執(zhí)行的業(yè)務(wù)邏輯放入SvcDoRun函數(shù)中晰搀,如下代碼中executeJob()函數(shù)即為我們定時(shí)執(zhí)行的任務(wù)
class WeatherPythonService(win32serviceutil.ServiceFramework):
_svc_name_ = "weather_service_test4"
_svc_display_name_ = "weather_service_test4"
_svc_description_ = "i am a test weather_service_test"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.run = True
def SvcStop(self):
# Before we do anything, tell the SCM we are starting the stop process.
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# And set my event.
win32event.SetEvent(self.hWaitStop)
self.run = False
def SvcDoRun(self):
#what to do#
while self.run:
executeJob()
time.sleep(5)
#win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
if __name__ == '__main__':
#executeJob()
win32serviceutil.HandleCommandLine(WeatherPythonService)
- 任務(wù)執(zhí)行函數(shù)
def executeJob():
now_time = time.localtime(time.time())
now_hour = now_time.tm_hour
now_minute = now_time.tm_min
for job in my_jobs:
ts = job['time']
for t in ts.split(','):
jobtime = t.split('.')
h = jobtime[0]
m = jobtime[1]
if (now_hour != h and now_minute != m):
code = city_code.find_code(job['city'])
wea = getWeath(code)
strWea = strDic(wea)
title = '{}天氣預(yù)報(bào)'.format(job['city'])
send_email(job['receivers'], 'title', title + ":\n" + strWea)
任務(wù)執(zhí)行時(shí)五辽,需要配置任務(wù)執(zhí)行列表,即上述代碼中my_jobs對(duì)象外恕,該對(duì)象是一個(gè)標(biāo)準(zhǔn)的json串杆逗,不同于上一篇文章的json格式,本篇文章的任務(wù)參數(shù)如下鳞疲,任務(wù)整體是一個(gè)數(shù)組罪郊,數(shù)組中包含了任務(wù)對(duì)象,每一個(gè)對(duì)象由3個(gè)字段組成尚洽,分別是郵件接收者郵箱receivers排龄、爬取城市city和爬取時(shí)間time
my_jobs = [{
"receivers":['1134024095@qq.com'],
"city":"昌平",
"time":"6.30,17.30"
},{
"receivers":['1134024095@qq.com'],
"city":"海淀",
"time":"6.30,17.30"
}]
- 安裝服務(wù),成功啟動(dòng)后翎朱,但是任務(wù)沒(méi)有正常執(zhí)行,可以通過(guò)查看系統(tǒng)任務(wù)事件來(lái)確定錯(cuò)誤的原因尺铣,如下圖所示拴曲,這是我在排查錯(cuò)誤的時(shí)候截圖
查詢系統(tǒng)日志:
win+r
回車輸入eventvwr.exe
在回車
四、發(fā)送郵件
這里我們使用QQ郵箱作為示例進(jìn)行演示凛忿,發(fā)送郵件使用smtplib庫(kù)
QQ郵箱發(fā)送需要申請(qǐng)口令澈灼,申請(qǐng)方式
選擇郵箱發(fā)送服務(wù)器
smtp.qq.com
和端口號(hào)465
構(gòu)造發(fā)件人、收件人和郵件內(nèi)容
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一點(diǎn)兒", sender]) # 括號(hào)里的對(duì)應(yīng)發(fā)件人郵箱昵稱店溢、發(fā)件人郵箱賬號(hào)
message['To'] = Header(','.join(receivers), 'utf-8')#接受者
message['Subject'] = Header(title, 'utf-8')
text為郵件內(nèi)容叁熔,通過(guò)From構(gòu)造發(fā)件人信息,To構(gòu)造收件人信息床牧,這個(gè)構(gòu)造的只是顯示的文本串荣回,如本小節(jié)底部截圖所示的收件人和發(fā)件人等,真正的接受郵件的賬號(hào)在發(fā)送郵件時(shí)指定戈咳。
- 連接郵箱服務(wù)器心软、登陸
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, mail_port) # mail_port 為 SMTP 端口號(hào)
smtpObj.login(mail_user, mail_pass)
- 發(fā)送郵件
smtpObj.sendmail(sender, receivers, message.as_string())
-
郵件發(fā)送成功
success.png
- 完整發(fā)送郵件代碼
# 三個(gè)參數(shù):第一個(gè)為文本內(nèi)容壕吹,第二個(gè) plain 設(shè)置文本格式,第三個(gè) utf-8 設(shè)置編碼
def send_email(receivers, title, text):
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一點(diǎn)兒", sender]) # 括號(hào)里的對(duì)應(yīng)發(fā)件人郵箱昵稱删铃、發(fā)件人郵箱賬號(hào)
message['To'] = Header(','.join(receivers), 'utf-8')#接受者
message['Subject'] = Header(title, 'utf-8')
ret = True
try:
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, mail_port) # mail_port 為 SMTP 端口號(hào)
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
except smtplib.SMTPException:
ret = False
f = open('./sendemail_weather.log', 'a', encoding = 'utf-8')
if ret:
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ':郵件發(fā)送成功\n')
else:
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +':無(wú)法發(fā)送郵件\n')
f.close()
- 測(cè)試發(fā)送郵件
send_email(['1134024095@qq.com','1024068757@qq.com'], "昌平", "6.30")
五耳贬、源代碼
以前寫(xiě)博客測(cè)試程序都是放在csdn,最近幾次發(fā)現(xiàn)csdn審核流程太慢了猎唁,導(dǎo)致和博客發(fā)布時(shí)間不統(tǒng)一咒劲,因此后續(xù)測(cè)試程序代碼我都盡量放在git上,本篇文章的測(cè)試程序有需要的朋友可以去weather_report_service下載
轉(zhuǎn)載聲明:本站文章無(wú)特別說(shuō)明诫隅,皆為原創(chuàng)腐魂,版權(quán)所有,轉(zhuǎn)載請(qǐng)注明:朝十晚八 or Twowords