Python-定時爬取指定城市天氣(二)-郵件提醒
目錄
一、概述
上一篇文章 ofollow,noindex" target="_blank">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":['[email protected]'], "city":"昌平", "time":"6.30,17.30" },{ "receivers":['[email protected]'], "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(['[email protected]','[email protected]'], "昌平", "6.30")
五、原始碼
以前寫部落格測試程式都是放在csdn,最近幾次發現csdn稽核流程太慢了,導致和部落格釋出時間不統一,因此後續測試程式程式碼我都儘量放在git上,本篇文章的測試程式有需要的朋友可以去 weather_report_service 下載
本篇文章是使用markdown語法寫的,排版實在不怎麼樣,大家湊合看吧,部落格園的markdown直譯器沒有簡書好,簡書地址 Python-定時爬取指定城市天氣(二) - 郵件提醒
轉載宣告:本站文章無特別說明,皆為原創,版權所有,轉載請註明:朝十晚八 or Twowords