python電子郵件系列(二)之SMTP發(fā)送郵件

郵件發(fā)送與接收的流程

SMTP是發(fā)送郵件的協(xié)議,是MUA(Mail User Agent)發(fā)送到MTA(Mail Transfer Agent)和MTA之間傳輸?shù)膮f(xié)議。

編寫程序來發(fā)送和接收郵件,本質(zhì)上就是以下這樣的過程:

發(fā)件人->MUA->MTA->若干MTA->MDA->收件人

其中,你是新浪的用戶,就會先發(fā)到新浪的MTA上,經(jīng)過若干MTA,會到達(dá)收件人的郵件服務(wù)商的MTA上,這個MTA會把郵件投遞到最終目的地MDA(Mail Delivery Agent)郵件投遞代理,Email到達(dá)MDA后,會靜靜地躺在郵件服務(wù)商的服務(wù)器上,等待用戶開機(jī)聯(lián)網(wǎng)通過MUA從MDA把郵件取到本地。

幾種郵件類型

純文本郵件/HTML文本郵件

發(fā)送純文本郵件:

先構(gòu)造郵件消息(用到email庫):

構(gòu)造一個最簡單的純文本郵件:

from email.mime.text import MIMEText
msg = MIMEText('send by simon...', 'plain', 'utf-8')

構(gòu)造MIMEText對象時,第一個參數(shù)就是郵件正文,第二個是子格式,plain表示傳入的是純文本,另一個值是html。

charset值為utf-8,保證多語言兼容性。此值也將被添加到Content-Type頭中去。

此外,若charset值設(shè)定,則Content-Transfer-Encoding會被設(shè)定為對應(yīng)值,如utf-8對應(yīng)base64。關(guān)于Content-Transfer-Encoding內(nèi)容在上一篇電子郵件系列(一)中已經(jīng)詳細(xì)介紹過。

當(dāng)我們print這個msg會發(fā)現(xiàn)其信件內(nèi)容(不含header)已經(jīng)是base64格式了。所以,當(dāng)指定了charset后,會指定Content-Type-Encoding,信件內(nèi)容相應(yīng)的由字符編碼為bytes,其本質(zhì)是二進(jìn)制,之后再將二進(jìn)制轉(zhuǎn)化為base64。

接下來,發(fā)送郵件(用到smtplib庫):

import smtplib


from_addr = '1005121394@qq.com'
to_addr = 'simonkindle@126.com'
password = 'your password'
smtp_server = 'smtp.qq.com'
server_port = 465

server = smtplib.SMTP_SSL(smtp_server, server_port)
server.set_debuglevel(1)  #打印出所有和SMTP服務(wù)器交互的信息
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

對于QQ郵箱來說,其要求SMTP發(fā)送郵件時必須使用SSL加密,所以,使用SSL加密端口465,正常情況下不要求SSL加密的使用25端口,同時創(chuàng)建smtp對象時使用的是smtp.SMTP函數(shù)。

sendmail的第二個參數(shù)to_addrs接收的是一個list,可以傳入多個收件人地址。第三個是郵件內(nèi)容,as_astring函數(shù)會將格式化的message對象(包含MIMEHeader以及內(nèi)容)全部轉(zhuǎn)化為str,這樣才能在網(wǎng)絡(luò)上傳輸(當(dāng)然,網(wǎng)絡(luò)上傳輸?shù)氖沁@些str的字節(jié)流bytes格式,這一過程未在代碼中體現(xiàn),個人猜測應(yīng)該在sendmail中完成。)

發(fā)送HTML格式郵件:

只需要在構(gòu)建message時將傳入的文本格式改為html,并在傳入html格式的文本即可。例如:

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

以上郵件我們還沒有為其添加郵件頭,也就是主題,發(fā)件人,收件人,這會造成一些問題,比如會提示不在收件人中,或收件人無

尤其是,當(dāng)你用網(wǎng)易郵箱發(fā)郵件時,會提示錯誤,原因就是你的sendmail函數(shù)中的發(fā)收件人與郵件頭中的不匹配。

添加郵件頭:

def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')

msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
msg['From'] = _format_addr('Python愛好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理員 <%s>' % to_addr)
msg['Subject'] = Header('來自SMTP的問候……', 'utf-8').encode()

我們定義了一個_format_addr格式化郵件地址,不能直接傳入name <addr@example.com>,因為name如果包含中文,需要對name進(jìn)行編碼。

但是,subject經(jīng)測試是可以直接傳入中文字符的。

同時支持HTML和plain格式的文本郵件

如果我們發(fā)送HTML郵件,收件人通過瀏覽器或者Outlook之類的軟件是可以正常瀏覽郵件內(nèi)容的,但是,如果收件人使用的設(shè)備太古老,查看不了HTML郵件怎么辦?

可以在發(fā)送HTML文本的同時再附加一個純文本,如果收件人的設(shè)備不支持查看HTML格式郵件,可以顯示純文本(兩個只能顯示一個)

我們可以創(chuàng)建一個MIMEMultipart類型對象,subtype子類型是'alternative',在上一篇文章中我們已經(jīng)講過,這代表純文本和HTML文本混合的內(nèi)容。
創(chuàng)建之后再向其中添加兩個MIMTText對象即可。

代碼如下:

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

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

發(fā)送附件

如何發(fā)送帶附件比如像圖片或mp3這種二進(jìn)制文件的郵件呢?

同樣,我們可以先構(gòu)造一個MIMEMultipart對象,向其中添加MIMEText對象作為郵件正文,再向其中添加MIMEBase作為附件即可。

msg = MIMEMultipart()
msg.attach(MIMEText('你好', 'plain', 'utf-8'))

# 添加附件就是添加一個MIMEBase對象,注意文件要以二進(jìn)制形式打開。
with open('F://my pictures//beauty//222.JPG', 'rb') as f:
    mime = MIMEBase('image', 'jpeg', filename='222.JPG')
    # 添加文件頭,此filename會顯示在郵件附件中
    mime.add_header('Content-Disposition', 'attachment', filename='222.JPG')
    # 定義圖片ID,以便在文本中引用
    mime.add_header('Content-ID', '<0>')
    # 讀入附件內(nèi)容
    mime.set_payload(f.read())
    # Encode the message's payload in Base64. 并添加Content-Transfer-Encoding頭信息
    encoders.encode_base64(mime)
    msg.attach(mime)

將圖片嵌入郵件正文中

如何把圖片附件嵌入郵件正文中(郵件服務(wù)商一般不允許使用HTML圖片外鏈,防止有害信息):

要把圖片嵌入到郵件正文中,我們只需按照發(fā)送附件的方式,先把郵件作為附件添加進(jìn)去,然后,在HTML中通過引用src="cid:0"就可以把附件作為圖片嵌入了。如果有多個圖片,給它們依次編號,然后引用不同的cid:x即可。

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

再次發(fā)送,即可在正文中看到圖片了。

加密郵件

使用標(biāo)準(zhǔn)的25端口連接SMTP服務(wù)器時,使用的是明文傳輸,發(fā)送郵件的整個過程可能會被竊聽。要更安全地發(fā)送郵件,可以加密SMTP會話,實際上就是先創(chuàng)建SSL安全連接,然后再使用SMTP協(xié)議發(fā)送郵件

比如騰訊qq郵箱要求SMTP服務(wù)必須要SSL加密。端口號是465。

我們上文所用的smtp.SMTP_SSL(server_addr, server_port)就是加密方式

非加密方式使用的函數(shù)是smtp.SMTP(server_addr, 25)

總結(jié)

對于非ASCII編碼的純文本,由輸入的字符到網(wǎng)絡(luò)傳輸這一過程中的編碼變化為:

str->bytes->base64->str->bytes

其中,前三步由MIMEText函數(shù)完成,base64->stras_string函數(shù)完成。最后一步不在代碼中體現(xiàn)。

對于二進(jìn)制文件來說:

binary code->base64->str->bytes

其中,binary codeopen函數(shù)以rb模式讀入,二進(jìn)制轉(zhuǎn)到base64encoders.encode_base64函數(shù)完成,之后與純文本相同,由as_string完成message格式到str的轉(zhuǎn)化。

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

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

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