【Python入門】40.電子郵件之 SMTP發(fā)送郵件

摘要:電子郵件是怎樣實(shí)現(xiàn)傳輸?shù)??如果通過SMTP協(xié)議,使用Python來發(fā)送電子郵件?


*寫在前面:為了更好的學(xué)習(xí)python,博主記錄下自己的學(xué)習(xí)路程。本學(xué)習(xí)筆記基于廖雪峰的Python教程,如有侵權(quán),請告知?jiǎng)h除。歡迎與博主一起學(xué)習(xí)Pythonヽ( ̄▽ ̄)? *


目錄

電子郵件
SMTP發(fā)送郵件
純文本郵件
帶HTML的郵件
帶附件的郵件
帶圖片的郵件
同時(shí)支持plain和HTML
SMTP加密
小結(jié)

電子郵件

email歷史悠久,直到現(xiàn)在都應(yīng)用廣泛。

在本節(jié)內(nèi)容中,將教大家使用Python發(fā)送和接收email。

在此之前先了解一下電子郵件的發(fā)送過程吧。

假設(shè)我們自己的郵件地址是myemail@163.com,對(duì)方的郵件地址是friend@qq.com,我們在網(wǎng)易提供的163郵箱界面編寫郵件,然后發(fā)送給對(duì)方。那么,這封郵件是怎么從我方發(fā)送到對(duì)方的呢?

(ps:這里的郵箱名字純屬虛構(gòu)。)

首先,我們稱用于編寫郵件的軟件或網(wǎng)頁為MUA(Mail User Agent),即郵件用戶代理

MUA會(huì)把郵件發(fā)送到MTA(Mail Transfer Agent),即郵件傳輸代理。就是email服務(wù)提供商如騰訊、網(wǎng)易等。在這里由于我們的郵箱是163郵箱,所以優(yōu)先把郵件投遞到網(wǎng)易提供的MTA里。

隨后,網(wǎng)易提供的MTA把郵件發(fā)送到對(duì)方服務(wù)商提供的MTA,這里是騰訊提供的MTA。這過程中會(huì)路過其他的MTA。

騰訊的MTA收到郵件后,把郵件投遞到最終目的地MDA(Mail Delivery Agent),即郵件投遞代理,在這里就是qq郵箱了。

最后,郵件會(huì)一直存放在對(duì)方地址的qq郵箱里,等待對(duì)方使用相應(yīng)的MUA進(jìn)行查詢。

總結(jié)一下郵件的發(fā)送過程:

發(fā)件人-->MUA-->(己方)MTA-->(其他)MTA-->(對(duì)方)MTA-->MDA<--MUA<--收件人

所以我們要通過編程實(shí)現(xiàn)電子郵件的發(fā)送與接收,對(duì)應(yīng)的就是

1.編寫MUA把郵件發(fā)送到MTA,
2.編寫MUA從MDA從接收郵件。

發(fā)郵件時(shí),MUA和MTA使用的是SMTP協(xié)議(Simple Mail Transfer Protocol)。

收郵件時(shí),MUA和MDA使用的是POP協(xié)議(Post Office Protocol)IMAP協(xié)議(Internet Message Access Protocol)。

接下來,介紹如何用Python編寫電子郵件的發(fā)送與接收。

SMTP發(fā)送郵件

郵件客戶端軟件在發(fā)郵件時(shí),會(huì)讓你先配置SMTP服務(wù)器。

同樣的我們在編寫MUA時(shí),需要輸入郵箱地址、密碼、收件人地址信息,以及登錄相對(duì)應(yīng)的SMTP服務(wù)器,比如qq郵箱就是smtp.qq.com,163郵箱就是smtp.163.com。

需要注意的是,在編寫MUA之前,要在對(duì)應(yīng)的郵箱開啟SMTP服務(wù),開啟后會(huì)得到授權(quán)碼,這個(gè)授權(quán)碼在登錄SMTP服務(wù)器時(shí)要用到。

編寫MUA用到Python的兩個(gè)模塊:emailsmtplib。

其中email負(fù)責(zé)構(gòu)造郵件,smtplib負(fù)責(zé)發(fā)送郵件。

純文本郵件

我們以最簡單的純文本郵件為例。

首先,通過MIMEText()構(gòu)造一個(gè)純文本郵件:

from email.mime.text import MIMEText
msg = MIMEText('Hello, my friend...', 'plain', 'utf-8')

這里有三個(gè)參數(shù),第一個(gè)參數(shù)為郵件正文,第二個(gè)參數(shù)為郵件類型,傳入'plain'表示純文本,第三個(gè)參數(shù)'utf-8'是編碼,以保證兼容多語言。

然后,編寫用戶信息輸入部分:

from_addr = input('From: ')                # 輸入郵件地址
password = input(Password: ')              # 輸入密碼
to_addr = input('To: ')                    # 輸入收件人地址
smtp_server = input('SMPT server: ')       # 輸入SMTP服務(wù)器地址

依次輸入的信息為:發(fā)件人郵箱地址、授權(quán)碼、收件人地址、SMTP服務(wù)器地址。

然后通過SMTP發(fā)送出去:

import smtplib
server = smtplib.SMTP(smtp_server, 25)     # SMTP協(xié)議默認(rèn)端口是25
server.set_debuglevel(1)                   # 打印和SMTP服務(wù)器交互的所有信息
server.login(from_addr, password)          # 登錄SMTP服務(wù)器
server.sendmail(from_addr, [to_addr], msg.as_string())   #發(fā)郵件
server.quit()

其中set_debuglevel(1)可以打印和SMTP服務(wù)器交互的所有信息。

login()方法用來登錄SMTP服務(wù)器,需要輸入郵箱地址和密碼。

sendmail()方法用來發(fā)送郵件,輸入三個(gè)參數(shù),第一個(gè)參數(shù)表示郵箱地址,第二個(gè)參數(shù)是一個(gè)list,表示收件人地址,可以多個(gè);第三個(gè)參數(shù)是郵件正文,這里as_string()是把MIMEText對(duì)象變成str。

執(zhí)行完整的代碼,我們就能在收件人郵箱收到這樣的郵件:


電子郵件1.png

會(huì)發(fā)現(xiàn)這封郵件沒有主題、發(fā)件人沒有顯示昵稱(只是顯示發(fā)件人的郵箱),沒有收件人。

我們需要在MIMEText中添加主題、發(fā)件人、收件人的信息,以形成完整的郵件:

from email.mime.text import MIMEText
from email import encoders
from email.header import Header
from email.utils import parseaddr, formataddr

import smtplib

def _format_addr(s):
    name, addr = parseaddr(s)                      # 解析郵件地址,name為郵件人,addr為郵件地址
    return formataddr((Header(name, 'utf-8').encode(), addr))      # 格式化郵件地址
    
from_addr = input('From: ')                # 輸入郵件地址
password = input(Password: ')              # 輸入密碼
to_addr = input('To: ')                    # 輸入收件人地址
smtp_server = input('SMPT server: ')       # 輸入SMTP服務(wù)器地址
    
msg = MIMEText('Hello, my friend...', 'plain', 'utf-8')
msg['From'] = _format_addr('三貝 <%s>' % from_addr)     # 添加發(fā)件人
msg['To'] = _format_addr('管理員 <%s>' % to_addr)       # 添加收件人
msg['Subject'] = Header('SMTP測試', 'utf-8').encode()   # 添加郵件主題

server = smtplib.SMTP(smtp_server, 25)             # SMTP協(xié)議默認(rèn)端口是25
server.set_debuglevel(1)                           # 打印和SMTP服務(wù)器交互的所有信息
server.login(from_addr, password)                  # 登錄SMPT服務(wù)器
server.sendmail(from_addr, [to_addr], msg.as_string())   #發(fā)郵件
server.quit()

其中函數(shù)_format_addr()是用來格式化郵件地址的。

發(fā)件人或收件人的顯示格式是name<addr@example.com>,但是我們不能直接傳入這樣一個(gè)字符串,因?yàn)槿绻兄形牡脑?,還要進(jìn)行編碼。

需要注意的是,這里的msg['To']傳入的不是一個(gè)list,如果有多個(gè)收件人,直接用,分隔即可。

這時(shí)候收到的郵件有顯示發(fā)件人、收件人和主題了:


電子郵箱2.png

帶HTML的郵件

想要在郵件內(nèi)容中添加HTML,在構(gòu)造MIMEText對(duì)象時(shí),把HTML字符串傳進(jìn)去即可,像這樣:

msg = MIMEText('<html><body><h1>Hello, my friend...</h1>' +
    '<p>send by <a  +
    '</body></html>', 'html', 'utf-8')

這里注意要把第二個(gè)參數(shù)'plain'改為'html'。

發(fā)送結(jié)果:


電子郵件3.png

帶附件的郵件

想要在郵件中添加附件。首先需要構(gòu)造一個(gè)MIMEMultipart對(duì)象表示郵件整體,然后添加一個(gè)MIMEText作為郵件正文,添加一個(gè)MIMEBase對(duì)象作為附件即可:

from email.mime.multipart import MIMEMultipart    # 再引入兩個(gè)必要模塊
from email.mime.base import MIMEBase

msg = MIMEMultipart()                                # 創(chuàng)建MIMEMultipart主體對(duì)象
msg['From'] = _format_addr('三貝 <%s>' % from_addr)
msg['To'] = _format_addr('管理員 <%s>' % to_addr)
msg['Subject'] = Header('SMTP測試', 'utf-8').encode()

msg.attach(MIMEText('Hello, my friend...', 'plain', 'utf-8'))

with open('c:/Users/Administrator/test.png', 'rb') as f:     # 打開需要添加的附件
    mime = MIMEBase('image', 'png', filename='test.png')     # 創(chuàng)建MIMEBase對(duì)象,即附件內(nèi)容
    mime.add_header('Content-disposition', 'attachment', filemane=.test.png')    # 添加必要的頭文件
    mime.add_header('Content-ID', '<0>')
    mime.add_header('X-Attachment-ID', '0')
    mime.set_payload(f.read())                               # 讀進(jìn)附件的內(nèi)容
    encoders.enode_base64(mime)                              # 用base64編碼
    msg.attach(mime)                                         # 把附件添加到主體對(duì)象中

然后,按正常的郵件發(fā)送流程補(bǔ)充代碼即可。


電子郵件4.png

帶圖片的郵件

如果想要在郵件正文中顯示圖片呢?我們需要先把圖片作為附件,然后在HTML中通過引用src="cid:0"就可以把附件作為圖片嵌入了。

msg.attach(MIMEText('<html><body><h1>Hello, my friend...</h1>' +
    '<p><img src="cid:0"></p>' +
    '</body></html>', 'html', 'utf-8'))

這個(gè)時(shí)候圖片不再作為附件發(fā)送,而是直接在郵件正文中顯示。

如果有多張圖片,則給它們編號(hào)后,引用不同的cid:x即可。

電子郵件5.png

同時(shí)支持plain和HTML

通過創(chuàng)建MIMEMultipart對(duì)象,我們可以讓郵件內(nèi)容即支持plain,也支持HTML。

在創(chuàng)建MIMEMultipart時(shí)指定subtypealternative

...
...
msg = MIMEMultipart('alternative')
msg['From'] = ...
msg['To'] = ...
msg['Subject'] = ...

msg.attach(MIMEText('Hello, my friend...', 'plain', 'utf-8'))
msg.attach(MIMEText('<html><body><h1>Hello, my friend...</h1></body></html>', 'html', 'utf-8'))
...
...

一旦收件人無法查看HTML格式的郵件,就會(huì)自動(dòng)變?yōu)閜lain格式查看。

SMTP加密

使用標(biāo)準(zhǔn)的25端口連接SMTP服務(wù)器時(shí),使用的是明文傳輸,在發(fā)送的過程有可能被竊聽。

想要給郵件加密,添加starttls()方法即可:

...
...
server = smtplib.SMTP(smtp_server, 25)
server.starttls()
...
...

在創(chuàng)建了SMTP對(duì)象后,立即調(diào)用starttls()方法即可。

小結(jié)

構(gòu)造一個(gè)郵件對(duì)象就是一個(gè)Message對(duì)象。而文本郵件對(duì)象是MIMEText,圖像郵件對(duì)象是MIMEImage,組合郵件對(duì)象是MIMEMultipart,MIMEBase可以表示任意郵件對(duì)象,它們的繼承關(guān)系如下:

 Message
 +- MIMEBase
    +- MIMEMultipart
    +- MIMENonMultipart
       +- MIMEMessage
       +- MIMEText
       +- MIMEImage

詳細(xì)內(nèi)容可以查看官方文檔:email.mime文檔。


以上就是本節(jié)的全部內(nèi)容,感謝你的閱讀。

下一節(jié)內(nèi)容:電子郵件之 POP3收取郵件

有任何問題與想法,歡迎評(píng)論與吐槽。

和博主一起學(xué)習(xí)Python吧( ̄▽ ̄)~*

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容