SMTP發(fā)送郵件


layout: post
title: 電子郵件系列(二)
subtitle: SMTP發(fā)送郵件
date: 2018-03-03
author: Simon
header-img: img/post-bg-git-learn.jpg
catalog: true
tags:

  • MIME
  • 電子郵件
  • SMTP

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

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

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

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

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

幾種郵件類型

純文本郵件/HTML文本郵件

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

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

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

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

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

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

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

當(dāng)我們print這個(gè)msg會(huì)發(fā)現(xiàn)其信件內(nèi)容(不含header)已經(jīng)是base64格式了。所以,當(dāng)指定了charset后,會(huì)指定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 = 'zotggqxvhhktbfig'
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()

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

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

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

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

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

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

[圖片上傳失敗...(image-df844d-1520222326027)]

[圖片上傳失敗...(image-f7aa6e-1520222326027)]

尤其是,當(dāng)你用網(wǎng)易郵箱發(fā)郵件時(shí),會(huì)提示錯(cuò)誤,原因就是你的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()

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

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

同時(shí)支持HTML和plain格式的文本郵件

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

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

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

代碼如下:

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)造一個(gè)MIMEMultipart對(duì)象,向其中添加MIMEText對(duì)象作為郵件正文,再向其中添加MIMEBase作為附件即可。

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

# 添加附件就是添加一個(gè)MIMEBase對(duì)象,注意文件要以二進(jìn)制形式打開。
with open('F://my pictures//beauty//222.JPG', 'rb') as f:
    mime = MIMEBase('image', 'jpeg', filename='222.JPG')
    # 添加文件頭,此filename會(huì)顯示在郵件附件中
    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"就可以把附件作為圖片嵌入了。如果有多個(gè)圖片,給它們依次編號(hào),然后引用不同的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ù)器時(shí),使用的是明文傳輸,發(fā)送郵件的整個(gè)過程可能會(huì)被竊聽。要更安全地發(fā)送郵件,可以加密SMTP會(huì)話,實(shí)際上就是先創(chuàng)建SSL安全連接,然后再使用SMTP協(xié)議發(fā)送郵件

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

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

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

總結(jié)

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

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

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

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

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

  • SMTP是發(fā)送郵件的協(xié)議,Python內(nèi)置對(duì)SMTP的支持,可以發(fā)送純文本郵件、HTML郵件以及帶附件的郵件。 P...
    牛崽兒酷閱讀 794評(píng)論 1 2
  • 本文介紹python發(fā)送郵件模塊smtplib以及相關(guān)MIME模塊。smtplib用于生成郵件發(fā)送的代理,發(fā)送郵件...
    戀戀風(fēng)辰閱讀 798評(píng)論 0 3
  • 最近一直糾結(jié)要使用哪個(gè)郵件接口比較好,幸得高人指教,使用smtp就可以發(fā)送郵件了。 本人開發(fā)項(xiàng)目使用的是think...
    東風(fēng)依舊閱讀 876評(píng)論 0 2
  • 使用python發(fā)送郵件的流程: 1、獲得smtp服務(wù) 2、連接服務(wù)器 3、模擬客戶端登錄 4、實(shí)現(xiàn)郵件發(fā)送 5、...
    henYee閱讀 385評(píng)論 1 0
  • 寫在前面 初學(xué)python,今天和昨晚折騰了一晚上,終于成功發(fā)送出了郵件!心情激動(dòng),做個(gè)記錄總結(jié)。 首先,要實(shí)現(xiàn)發(fā)...
    河里的肥魚閱讀 4,592評(píng)論 0 3

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