Python-定時爬取指定城市天氣(二)-郵件提醒


一、概述

上一篇文章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 在回車

![查詢系統日志](https://www.cnblogs.com/images/cnblogs_com/swarmbees/1351403/t_look_log.png)

四、發送郵件

這里我們使用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.、郵件發送成功

![郵件發送成功](https://www.cnblogs.com/images/cnblogs_com/swarmbees/1351403/t_success.png)

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-定時爬取指定城市天氣(二) - 郵件提醒

如果您覺得文章不錯,不妨給個 打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!




很重要--轉載聲明

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM