自动化运维之使用Python3收发电子邮件~~~附源码
一、背景介紹
1.1 一些專業(yè)名稱的解釋
MUA——Mail User Agent,郵件用戶代理。是用戶與電子郵件系統(tǒng)的交互接口,一般來說它就是我們PC機上的一個程序,提供一個好的用戶界面,它提取用戶在其界面填寫的各項信息,生成一封符合SMTP等郵件標準的郵件,然后采用SMTP協(xié)議將郵件發(fā)送到發(fā)送端郵件服務器。常見的通用型代理有OutLook Express、FoxMail、網(wǎng)易閃電郵等
MTA——Mail Transfer Agent,郵件傳輸代理。負責郵件的傳輸,它采用端到端的傳輸?shù)膫鬏敺绞剑炊酥鳈C參與郵件傳輸?shù)娜^程,也可稱之為SMTP服務器。其實就是那些Email服務提供商,比如網(wǎng)易、新浪等等。比如我使用的電子郵件是163.com,所以,發(fā)Email首先被投遞到網(wǎng)易提供的MTA,再由網(wǎng)易的MTA發(fā)到對方服務商,可能是sina、qq或者其他的MTA,這個過程中間可能還會經(jīng)過別的MTA
MDA——Mail Deliver Agent,郵件投遞代理。Email到達收件人(比如使用的是新浪的郵箱)的MTA后,新浪的MTA會把Email投遞到郵件的最終目的地MDA。Email到達MDA后,就靜靜地躺在新浪的某個服務器上,存放在某個文件或特殊的數(shù)據(jù)庫里。Email不會直接到達對方的電腦,對方要取到郵件,必須通過MUA從MDA上把郵件取到自己的電腦上。
SMTP——Simple Mail Transfer Protocol,簡單郵件傳輸協(xié)議 。是Internet上基于TCP/IP的應用層協(xié)議,定義了郵件發(fā)送方和接收方之間的連接傳輸,默認端口號TCP25。SMTP有其一定的局限性,它只能傳送ASCII文本文件,而對于一些二進制數(shù)據(jù)文件需要進行編碼后才能傳送。
MIME——Multipose Internet Mail Extensions,多途徑Internet郵件擴展協(xié)議。是一種編碼標準,它解決了SMTP只能傳送ASCII文本的限制。MIME定義了各種類型數(shù)據(jù),如聲音、圖像、表格、二進制數(shù)據(jù)等的編碼格式,通過對這些類型的數(shù)據(jù)編碼并將它們作為郵件中的附件進行處理,以保證這些部分內容完整、正確地傳輸。因此,MIME增強了SMTP的傳輸功能,統(tǒng)一了編碼規(guī)范。
POP3——Post Office Protocol Version 3,郵局協(xié)議的第三版本。主要用于支持使用客戶端遠程管理在服務器上的電子郵件。,它規(guī)定怎樣將個人計算機連接到Internet的郵件服務器和下載電子郵件的電子協(xié)議。它是因特網(wǎng)電子郵件的第一個離線協(xié)議標準,POP3允許用戶從服務器上把郵件存儲到本地主機(即自己的計算機)上,同時刪除保存在郵件服務器上的郵件
IMAP——Internet Message Access Protocol,因特網(wǎng)報文存取協(xié)議。它是跟POP3類似郵件訪問標準協(xié)議之一。不同的是,開啟了IMAP后,您在電子郵件客戶端收取的郵件仍然保留在服務器上,同時在客戶端上的操作都會反饋到服務器上,如:刪除郵件,標記已讀等,服務器上的郵件也會做相應的動作。所以無論從瀏覽器登錄郵箱或者客戶端軟件登錄郵箱,看到的郵件以及狀態(tài)都是一致的。
1.2 電子郵件收發(fā)原理
有了上述基本概念,要編寫程序來發(fā)送和接收郵件,本質上就是:
編寫MUA把郵件發(fā)到MTA;
編寫MUA從MDA上收郵件。
發(fā)郵件時,MUA和MTA使用的協(xié)議就是SMTP,后面的MTA到另一個MTA也是用SMTP協(xié)議。
收郵件時,MUA和MDA使用的協(xié)議有兩種:POP3和IMAP。
郵件客戶端軟件在發(fā)郵件時,會讓你先配置SMTP服務器,也就是你要發(fā)到哪個MTA上。假設你正在使用163的郵箱,你就不能直接發(fā)到新浪的MTA上,因為它只服務新浪的用戶,所以,你得填163提供的SMTP服務器地址:smtp.163.com,為了證明你是163的用戶,SMTP服務器還要求你填寫郵箱地址和郵箱口令,這樣,MUA才能正常地把Email通過SMTP協(xié)議發(fā)送到MTA。
類似的,從MDA收郵件時,MDA服務器也要求驗證你的郵箱口令,確保不會有人冒充你收取你的郵件,所以,Outlook之類的郵件客戶端會要求你填寫POP3或IMAP服務器地址、郵箱地址和口令,這樣,MUA才能順利地通過POP或IMAP協(xié)議從MDA取到郵件。
1.3 編碼前的準備工作
使用Python收發(fā)郵件前,請先準備好至少兩個電子郵件,如xxx@163.com,xxx@sina.com,xxx@qq.com等,注意兩個郵箱不要用同一家郵件服務商。
最后特別注意,目前大多數(shù)郵件服務商都需要手動打開SMTP發(fā)信和POP收信的功能,否則只允許在網(wǎng)頁登錄,以163郵箱為例配置步驟如下:
(1)登陸自己的163郵箱,在設置中選擇“POP3/SMTP/IMAP”,配置如下圖,并記住SMTP服務器地址,后續(xù)會用到
(2)找到設置中的“客戶端授權碼”,開啟這項功能并設置授權碼,系統(tǒng)會提示不允許與登陸密碼相同
二、用python3發(fā)郵件
SMTP是發(fā)送郵件的協(xié)議,Python對SMTP支持有smtplib和email兩個模塊,email負責構造郵件,smtplib負責發(fā)送郵件
2.1. 最簡單的示例,發(fā)送純文本郵件(無附件、正文里無圖片和html語句)
需要導入的包如下:(關于第一個包后續(xù)展開講,在這個最簡單的例子中使用這個)
from email.mime.text import MIMEText import smtplib
"""
發(fā)送頻繁的話會被當做垃圾郵件,運行程序顯示554錯誤
"""
from email.mime.text import MIMEText
from email.utils import formataddr
import smtplib
# 輸入發(fā)送人地址和口令:
from_addr = '***@163.com'
password = '***'#授權碼而非登陸密碼
# 輸入收件人地址:
to_addr = '***@qq.com'
# 輸入發(fā)件人SMTP服務器地址:
smtp_server = 'smtp.163.com'
#構造郵件
msg = MIMEText('這里寫正文的內容……','plain','utf-8') #構造MIMEText對象
msg['From'] = formataddr(["發(fā)件人的昵稱",from_addr]) #括號里的對應發(fā)件人郵箱昵稱、發(fā)件人郵箱賬號
msg['To'] = formataddr(["收件人的昵稱",to_addr]) #括號里的對應收件人郵箱昵稱、收件人郵箱賬號
msg['Subject'] = "這里寫主題" #郵件的主題,也可以說是標題
server = smtplib.SMTP(smtp_server, 25) # SMTP協(xié)議默認端口是25
#server.set_debuglevel(1)#打印出和SMTP服務器交互的所有信息
server.login(from_addr, password)#登錄SMTP服務器
#sendmail()方法就是發(fā)郵件,由于可以一次發(fā)給多個人,所以傳入一個list,郵件正文是一個str,as_string()把MIMEText對象變成str
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()#退出SMTP服務器
tips1:
from email.utils import formataddr中的formataddr使用help(formataddr)查看
可以看到formataddr和parseaddr是互逆的兩個過程
舉個例子:
formataddr(['我', 'learningatcisco-noreply@cisco.com'])的結果是:=?utf-8?b?5oiR?= <learningatcisco-noreply@cisco.com>
parseaddr('"我"<learningatcisco-noreply@cisco.com>')的結果是:'我', 'learningatcisco-noreply@cisco.com'
tips2:
password要填寫授權碼(也就是自己設置的),而不是郵箱登陸密碼
tips3:
構造MIMEText對象時,第一個參數(shù)就是郵件正文,第二個參數(shù)是MIME的subtype,傳入'plain'表示純文本,最終的MIME就是'text/plain',最后一定要用utf-8編碼保證多語言兼容性
tips4:
SMTP協(xié)議就是簡單的文本命令和響應。login()方法用來登錄SMTP服務器sendmail()方法就是發(fā)郵件,由于可以一次發(fā)給多個人(用“,”隔開),所以傳入一個list郵件正文是一個str,as_string()把MIMEText對象變成str
查看收件箱的結果如下:
為什么沒有顯示昵稱我也不太清楚……只試驗了自己的郵箱,并沒有進行大范圍的測試
2.2 第二個示例——郵件正文中包含html語句(當然也可以包含純文本文字)
在構造MIMEText對象時,把HTML字符串傳進去,再把第二個參數(shù)由plain變?yōu)?code>html就可以了
msg = MIMEText('<html><body><h1>Hello</h1>' +
'<p>send by <a +
'</body></html>', 'html', 'utf-8')#發(fā)送html
查看郵件結果:
2.3 發(fā)送不同格式的附件(當然也可以包含純文本)
基本思路就是,使用MIMEMultipart來標示這個郵件是多個部分組成的,然后attach各個部分。如果是附件,則add_header加入附件的聲明。
在python中,MIME的這些對象的繼承關系如下:
官方參考:https://docs.python.org/3/library/email.mime.html
一般來說,不會用到MIMEBase,而是直接使用它的繼承類。MIMEMultipart有attach方法,而MIMENonMultipart沒有,只能被attach。
MIME有很多種類型,這個略麻煩,如果附件是圖片格式,要用MIMEImage,如果是音頻,要用MIMEAudio等等……但是可以作為附件的文件類型實在是太多了,記起來又很麻煩,這里有一個懶人方法就是,不管什么類型的附件,都用MIMEApplication,MIMEApplication默認子類型是application/octet-stream。
application/octet-stream表明“這是個二進制的文件,希望你們那邊知道怎么處理”,然后客戶端,比如qq郵箱,收到這個聲明后,會根據(jù)文件擴展名來猜測。
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.image import MIMEImage
#from email.mime.image import MIMEAudio #并沒有找到MIMEAudio這個庫
# 如名字所示Multipart就是分多個部分 # 構造一個MIMEMultipart對象代表郵件本身
msg = MIMEMultipart()
# ---這是文字部分---
part1 = MIMEText("純文本內容")
msg.attach(part1)
# xlsx類型附件
part2 = MIMEApplication(open('癢癢鼠.xlsx', 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename="癢癢鼠.xlsx")
msg.attach(part2)
#圖片作為附件的時候,有時會出現(xiàn)圖片不完整的情況
# jpg類型附件
part3 = MIMEApplication(open('qqq.jpg', 'rb').read())
part3.add_header('Content-Disposition', 'attachment', filename="qqq.jpg")
msg.attach(part3)
# jpg類型附件另一種方法
part33 = MIMEImage(open('qqq.jpg', 'rb').read())
part33.add_header('Content-Disposition', 'attachment', filename="qqq.jpg")
msg.attach(part33)
# pdf類型附件
part4 = MIMEApplication(open('馬蜂窩廬山.pdf', 'rb').read())
part4.add_header('Content-Disposition', 'attachment', filename="馬蜂窩廬山.pdf")
msg.attach(part4)
# mp3類型附件
part5 = MIMEApplication(open('天哪.mp3', 'rb').read())
part5.add_header('Content-Disposition', 'attachment', filename="天哪.mp3")
msg.attach(part5)
# mp3類型附件另一種方法,因為無法導入MIMEAudio這個庫,失敗
總結一下用法,可直接套用以下模板:
from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication msg = MIMEMultipart()
#純文本內容 part1 = MIMEText("純文本內容") msg.attach(part1)
# 添加附件
part2 = MIMEApplication(open('附件***.類型', 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename="附件***.類型")
msg.attach(part2)
查看郵件結果:
tips1:可以添加多個附件
tips2:附件為圖片的時候有可能不完整
2.4 加密傳輸
使用標準的25端口連接SMTP服務器時,使用的是明文傳輸,發(fā)送郵件的整個過程可能會被竊聽。要更安全地發(fā)送郵件,可以加密SMTP會話,實際上就是先創(chuàng)建SSL安全連接,然后再使用SMTP協(xié)議發(fā)送郵件。
某些郵件服務商,例如Gmail,提供的SMTP服務必須要加密傳輸。我們來看看如何通過Gmail提供的安全SMTP發(fā)送郵件。
必須知道,Gmail的SMTP端口是587,因此,修改代碼如下:
smtp_server = 'smtp.gmail.com' smtp_port = 587 server = smtplib.SMTP(smtp_server, smtp_port) server.starttls() # 剩下的代碼和前面的一模一樣: server.set_debuglevel(1)
只需要在創(chuàng)建SMTP對象后,立刻調用starttls()方法,就創(chuàng)建了安全連接。后面的代碼和前面的發(fā)送郵件代碼完全一樣。
三、用python3收郵件
講完了發(fā)送郵件,現(xiàn)在講解收取郵件
收取郵件就是編寫一個MUA作為客戶端,從MDA把郵件獲取到用戶的電腦或者手機上。收取郵件最常用的協(xié)議是POP協(xié)議,目前版本號是3,俗稱POP3。
Python內置一個poplib模塊,實現(xiàn)了POP3協(xié)議,可以直接用來收郵件。
注意到POP3協(xié)議收取的不是一個已經(jīng)可以閱讀的郵件本身,而是郵件的原始文本,這和SMTP協(xié)議很像,SMTP發(fā)送的也是經(jīng)過編碼后的一大段文本。
要把POP3收取的文本變成可以閱讀的郵件,還需要用email模塊提供的各種類來解析原始文本,變成可閱讀的郵件對象。
所以,收取郵件分兩步:
第一步:用poplib把郵件的原始文本下載到本地;
第二部:用email解析原始文本,還原為郵件對象。
總結
以上是生活随笔為你收集整理的自动化运维之使用Python3收发电子邮件~~~附源码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sqlserver常用数据类型(精炼版)
- 下一篇: 了不起的修仙模拟器五行聚灵阵摆放技巧攻略