室友選修的《機器學習》,結課做業是對8萬+的數據進行分析。他向我抱怨,說這麼多數據至少得運算半個多小時,還得語法邏輯不出錯,否則得重來。
「總不能守着電腦吧,想出去玩,又拿不許程序準確結束時間。」
我想python做爲編程界的萬金油,幾乎不存在它不能染指的地方。因此有了如下基於smtp協議、利用python自帶模塊email,smtplib實現自動化發送郵件的代碼。python
有必要簡單瞭解一下SMTP的概念,能夠加深對代碼的理解。git
因此什麼是SMTP呢?
SMTP是簡單郵件傳輸協議,不過只能傳送可打印的7位ASCII碼郵件,這樣存在過大的侷限性。因而後來有了MIME,即通用互聯網郵件擴充。MIME在郵件首部中說明了郵件的數據類型(如文本,聲音,圖像,視像等),且經過MIME郵件能夠同時傳送多種類型的數據(附件)。github
一個郵件系統應該具備三個主要組成構建。用戶代理、郵件服務器,郵件發送協議(SMTP)和郵件讀取協議(POP3,又:郵局協議)
編程
SMTP客戶端(發送方的郵件服務器)每隔必定時間對郵件緩存掃描,若是發現有郵件,就用25端口與接收方的SMTP服務器創建TCP鏈接。鏈接創建後,接收方服務器發出「服務就緒」,SMTP客戶端向SMTP服務器發送HELO命令,附上發送方的主機名。SMTP服務器若是有能力接受郵件,就回復「250」(OK),若是服務不可用回覆「421」。緩存
略服務器
郵件發送完畢後,SMTP客戶發送QUIT命令。SMTP服務器返回「221」(服務關閉),表示贊成釋放TCP鏈接。郵件傳輸過程結果。app
1. SMTP不使用中間郵件服務器,必定是發送方服務器到接收方服務器
2. 爲何要經過郵件服務器而不是直接發送方用戶代理–>接收方用戶代理呢?緣由很簡單,被人給你發送郵件的時候你並不必定在機器學習
一個郵件分爲信封和內容兩大部分,內容包含首部、主體。
1. 首部(header),有規定格式,比較重要有如下:
「To」 接收方郵件地址
「Subject」 郵件主題
「From」 發送方郵件地址
2. 主體(body),容許用戶自由撰寫學習
attachment
,即以附件形式下載);disposition-parm表示文件保存的默認名有了以上對SMTP知識簡單的梳理以後,就能夠很容易理解後面的代碼。而這裏我是經過登錄本身的郵箱來實現發送郵件,這樣作的目的是:若是不登錄本身的郵箱,發送出去的郵件很容易被誤認爲垃圾郵件,或是被屏蔽,或是在垃圾箱,接收方不能及時收取。ui
python內置兩個模塊,email和smtplib,前者負責構建一封郵件,後者實現發送
基於python建立一封郵件,使用的是email模塊,我會用不嚴謹、但儘量生動的語言來解釋對象的內置方法
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
aLetter = MIMEMultipart() #實例化「一個包裹」
# attach(),往包裹裏面放東西
# MIMEText()能夠看作一封信,第一個參數表示內容,第二個表示類型,第三個表示編碼格式
aLetter.attach(MIMEText("hello,what are you doing?", "plain", "utf-8"))
# 在「包裹」上寫上發給誰(to),發的什麼(subject),誰發的(from)
aLetter["To"] = "xxxxx@qq.com"
aLetter["Subject"] = "Make A Friend"
aLetter["From"] = "myemail@qq.com"
# 實例化一封附加信
attachment = MIMEText("i miss you,and you?", "plain", "utf-8")
# 說明郵件數據的主題類型和子類型
attachment["Content-Type"] = "text/plain"
# 以附件形式下載,默認文件名爲happy.txt
attachment["Content-Disposition"] = "attachment;filename=happy.txt"
# 將附加信丟入包裹中
aLetter.attach(attachment)
print(aLetter) # 打印輸出效果
因而有了下面一個「包裹」
有了包裹,就須要郵寄出去,這裏須要用到smtplib模塊來發送郵件,流程以下:
# 實例化一個smtp對象
smtp = smtplib.SMTP_SSL()
# 鏈接郵件服務器
smtp.connect("smtp.qq.com")
# 登錄本身的郵箱
smtp.login("578******@qq.com", "htn******cga")
# 發送郵件
smtp.sendmail("578******@qq.com", "gu******995@163.com", aLetter.as_string())
# 退出鏈接
smtp.quit()
如此就是一個完整的發送郵件的過程
爲了更高效的利用代碼,爲了使代碼具備更好的擴展性,利用面向對象的思想來封裝代碼天然是不可少的。
首先我寫了一個AutoEmail的父類,其下有6個方法,依次對應:登錄信息,發送信息,生成郵件,生成附件,發送郵件,選擇服務器。
因爲網易163郵箱和QQ郵箱鏈接的服務器不一樣,前者「smtp.163.com」,後者「smtp.qq.com」,且網易針對smtplib.SMTP()實例化對象,而QQ郵箱須要針對smtp.SMTP_SSL()
固然,爲了對用戶更友好,這裏我寫了兩個派生類來繼承AutoEmail。
父類的choose_server()方法,經過區分實例化對象的類名,來提供相對應的方法
獲取實例化對象的類名:__class__.__name__
若是是網易郵箱
若是是QQ郵箱
代碼中的郵箱密碼,不是郵箱的真實代碼,而是受權密碼。
完整代碼請查看GitHub