一、概述
上一篇文章python-定時爬取指定城市天氣(一)-發送給關心的微信好友中我們講述了怎么定時爬取城市天氣,並發送給指定微信好友,文末遺留兩個問題
- 定時任務做成windows服務,這樣更優雅,隨開機啟動
- 發送消息給微信好友換成發送郵件給指定郵箱
本篇文章我們在原來代碼的基礎上進行了一定的模塊拆分,並處理以上兩個問題
二、模塊重新划分
1、 新增my_job.py文件,把任務模塊單獨划分出來
之前的定時任務使用的是apscheduler庫做的,並且任務類在main函數所在py文件中,這樣導致主py文件很難進行修改
2、 新增util.py文件
包含公用的方法,比如目前的字典轉字符串
3.、新增weather_service.py文件
主要負責構造windows服務,也是一個主py文件,不同於第一篇文章的主py文件weath_report.py,這是我們實現的兩種定時任務,可分別運行,如果想把天氣信息通知微信好友則啟動weath_report.py,可參考文章ython-定時爬取指定城市天氣(一)-發送給關心的微信好友,如果是通過發送郵件的方式則直接把weather_service.py安裝成windows服務,並啟動即可,記住需要配置運行的任務列表,下邊會講述怎么配置任務
4、 新增timing_task.py文件
包含任務方法executeJob(),主要是在服務中循環跑,然后在合適的時間爬取天氣並發送到指定郵箱,任務的參數是通過配置json串來實現
三、優化定時任務
本篇文章的定時任務是運行在windows服務中的,因此我們首先需要安裝pywin32模塊
1.、安裝pywin32
pip install pywin32
2.、服務操作相關命令
1.安裝服務 python PythonService.py install
2.讓服務自動啟動 python PythonService.py --startup auto install
3.啟動服務 python PythonService.py start
4.重啟服務 python PythonService.py restart
5.停止服務 python PythonService.py stop
6.刪除/卸載服務 python PythonService.py remove
3.、啟動服務時被拒絕
Installing service timingTaskDaemon
Error installing service: 拒絕訪問。 (5)
a.大多數原因是由於python環境配置的問題,python默認安裝時配置的pah是用戶環境變量,這里我們需要改成系統環境變量,具體可以參考Python 寫windows service 及 start service 出現錯誤 1053:服務沒有及時響應啟動或控制請求
b.考慮命令行是否有權限,我自己的win8系統默認權限就不夠,需要右鍵管理員啟動才可以
4、 實現windows服務功能,我們需要繼承win32serviceutil.ServiceFramework這個類,把需要執行的業務邏輯放入SvcDoRun函數中,如下代碼中executeJob()函數即為我們定時執行的任務
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)
5.、任務執行函數
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 = '{}天氣預報'.format(job['city'])
send_email(job['receivers'], 'title', title + ":\n" + strWea)
任務執行時,需要配置任務執行列表,即上述代碼中my_jobs對象,該對象是一個標准的json串,不同於上一篇文章的json格式,本篇文章的任務參數如下,任務整體是一個數組,數組中包含了任務對象,每一個對象由3個字段組成,分別是郵件接收者郵箱receivers、爬取城市city和爬取時間time
my_jobs = [{
"receivers":['1134024095@qq.com'],
"city":"昌平",
"time":"6.30,17.30"
},{
"receivers":['1134024095@qq.com'],
"city":"海淀",
"time":"6.30,17.30"
}]
6.、安裝服務,成功啟動后,但是任務沒有正常執行,可以通過查看系統任務事件來確定錯誤的原因,如下圖所示,這是我在排查錯誤的時候截圖
查詢系統日志:
win+r
回車輸入eventvwr.exe
在回車
四、發送郵件
這里我們使用QQ郵箱作為示例進行演示,發送郵件使用smtplib庫
1.、QQ郵箱發送需要申請口令,申請方式
2、 選擇郵箱發送服務器smtp.qq.com
和端口號465
3.、構造發件人、收件人和郵件內容
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一點兒", sender]) # 括號里的對應發件人郵箱昵稱、發件人郵箱賬號
message['To'] = Header(','.join(receivers), 'utf-8')#接受者
message['Subject'] = Header(title, 'utf-8')
text為郵件內容,通過From構造發件人信息,To構造收件人信息,這個構造的只是顯示的文本串,如本小節底部截圖所示的收件人和發件人等,真正的接受郵件的賬號在發送郵件時指定。
4.、連接郵箱服務器、登陸
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, mail_port) # mail_port 為 SMTP 端口號
smtpObj.login(mail_user, mail_pass)
5、 發送郵件
smtpObj.sendmail(sender, receivers, message.as_string())
6.、郵件發送成功
7、 完整發送郵件代碼
# 三個參數:第一個為文本內容,第二個 plain 設置文本格式,第三個 utf-8 設置編碼
def send_email(receivers, title, text):
message = MIMEText(text, 'plain', 'utf-8')
message['From'] = formataddr(["就差一點兒", sender]) # 括號里的對應發件人郵箱昵稱、發件人郵箱賬號
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 端口號
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') + ':郵件發送成功\n')
else:
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +':無法發送郵件\n')
f.close()
8、測試發送郵件
send_email(['1134024095@qq.com','1024068757@qq.com'], "昌平", "6.30")
五、源代碼
以前寫博客測試程序都是放在csdn,最近幾次發現csdn審核流程太慢了,導致和博客發布時間不統一,因此后續測試程序代碼我都盡量放在git上,本篇文章的測試程序有需要的朋友可以去weather_report_service下載
本篇文章是使用markdown語法寫的,排版實在不怎么樣,大家湊合看吧,博客園的markdown解釋器沒有簡書好,簡書地址Python-定時爬取指定城市天氣(二) - 郵件提醒
![]() |
![]() |
很重要--轉載聲明
-
本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords
-
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。