python要實現發送郵件的功能,需要使用smtplib庫。
1. 過程大致如下:
1. 建立和SMTP郵件服務器的連接
# 默認端口25 smtp = smtplib.SMTP(host, port) # 或者 smtp = smtplib.SMTP() smtp.connect(host, port) # 帶SSL,默認端口465 smtp = smtp.SMTP_SSL() smtp.connect(host, port)
2. 完成身份認證
# 對於163而言,第三方客戶端登陸使用的是授權碼而不是密碼 smtp.login(username, password)
3. 發送郵件(必須明確發件人、收件人、主題、內容,而抄送人、密送人、附件、內嵌式圖片等資源是可選的)
smtp.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
4. 結束會話
smtp.quit()
2. MIME相關知識介紹
MIME全稱Multipurpose Internet Mail Extensions(多用途互聯網郵件擴展)。是一個互聯網標准,它擴展了電子郵件標准,使其能夠支持:
- 非ASCII字符文本;
- 非文本格式附件(二進制、聲音、圖像等);
- 由多部分(multiple parts)組成的消息體;
- 包含非ASCII字符的頭信息(Header information)
此外,web中使用的HTTP協議也使用了MIMIE的框架,標准被擴展為互聯網媒體類型。
MIME頭部信息:
MIME版本: 目前版本1.0
MIME-Version: 1.0
內容類型(Content-Type),用於指定消息類型。 形式如下:
Content-Type: [type]/[subtype]; parameter
type有以下形式:
- Text: 文本消息, 可以使用charset參數指定字符集;
- Multipart: 用於連接消息體的多個部分構成一個消息,這些部分可以是不同類型的數據;
- Application:用於傳輸應用程序數據或者二進制數據;
- Message:用於包裝一個E-mail消息;
- Image:用於傳輸靜態圖片數據;
- Audio:用於傳輸音頻數據;
- Video:用於傳輸動態影像數據,可以是與音頻編輯在一起的視頻數據格式
常見的文件擴展名與Content-Type對應關系如下:
| 文件擴展名 | Content-Type |
|---|---|
| .txt | text/plain |
| .html | text/html |
| .xhtml | application/xhtml+xml |
| .css | text/css |
| .js | application/javascript |
| .xml | application/atom+xml |
| .json | application/json |
| .jpg | image/jpeg |
| .png | image/png |
| .gif | image/gif |
| .mp3 | audio/mpeg3 |
| .mp4 | video/mp4 |
| .wav | audio/wav |
| .avi | video/avi |
| .doc | application/msword |
| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| .xls | application/vnd.ms-excel |
| .xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| .ppt | application/vnd.ms-powerpoint |
| .pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
| application/pdf | |
| .rar | application/x-rar-compressed |
| .zip | application/x-compressed |
| .tar | application/x-tar |
| .gz | application/x-compressed |
| .bz2 | application/x-bzip2 |
| 表示任意二進制數據 | application/octet-stream |
| 使用HTTP的POST方法提交表單 | application/x-www-form-urlencoded |
| 主要用於表單提交時伴隨文件上傳的場合 | multipart/form-data |
Text默認是text/plain,Application默認是application/octet-stream而Multipart默認情況下被看作multipart/mixed。
內容傳輸編碼(Content-Transfer-Encoding)
Content-Transfer-Encoding: [mechanism]
其中,mechanism的值可以指定為“7bit”,“8bit”,“binary”,“quoted-printable”,“base64”
MIME信息剖析
一封普通的文本郵件的信息包含一個頭部分(例如:From、To、Subject 等等)和一個體部分。體部分通常為單體類型(例如:text、image、audio、video、application 等等)或是復合類型(即:multipart)。頭部分和體部分之間用一個空行進行分隔,並且體部分的類型由信頭內容類型字段 Content-Type 描述。
信頭含義 (Headers)
| 域名 | 含義 |
|---|---|
| Content-Type | 內容的類型 |
| MIME-Version | MIME 版本 |
| Content-Transfer-Encoding | 內容的傳輸編碼方式 |
| From | 發件人地址 |
| To | 收件人地址 |
| Cc | 抄送地址 |
| Bcc | 暗送地址 |
| Date | 日期和時間 |
| Subject | 主題 |
| Received | 傳輸路徑 |
| Return-Path | 回復地址 |
| Delivered-To | 發送地址 |
| Reply-To | 回復地址 |
| Message-ID | 消息 ID |
信體部分
-
郵件中常見的簡單類型有 text/plain(純文本)和 text/html(超文本)。
-
復雜的郵件內容格式采用 multipart 類型,可以包括純文本/超文本(alternative)、內嵌資源(圖片)(related)、附件類型(mixed)等等。
multipart 類型的郵件體被分為多個段,每個段又包含段頭和段體兩部分,這兩部分之間也以空行分隔。
信體頭含義
| 域名 | 含義 |
|---|---|
| Content-Type | 段體的類型 |
| Content-Transfer-Encoding | 段體的傳輸編碼方式 |
| Content-Disposition | 段體的位置,內嵌(inline)或附件(attachment) |
| Content-ID | 段體的 ID, 唯一標識 |
| Content-Location | 段體的位置(路徑) |
| Content-Base | 段體的基位置 |
常見的 multipart 類型有三種:multipart/mixed, multipart/related 和 multipart/alternative。
復合類型層次關系示例圖:

multipart 諸類型的共同特征是,在段頭指定 boundary 參數字符串,段體內的每個子段以此字符串定界。
所有的子段都以 --boundary 行開始,父段則以 --boundary-- 行結束。段與段之間也以空行分隔。
例子:
1) 發送純文本文件:
Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: base64 aGVsbG8gd29ybGQK5L2g5aW95LiW55WM
2)發送包含富文本的郵件:
Content-Type: multipart/alternative; boundary="----=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106" ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: base64 aGVsbG8gd29ybGTkvaDlpb3kuJbnlYw= ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: base64 PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij5oZWxsbyB3b3JsZDwvc3Bhbj48 L2Rpdj48ZGl2ICBzdHlsZT0iY2xlYXI6Ym90aDsiPjxzcGFuICBzdHlsZT0iZm9udC1mYW1pbHk6 VGFob21hLEFyaWFsLFNUSGVpdGksU2ltU3VuO2NvbG9yOiMwMDAwMDA7YmFja2dyb3VuZC1jb2xv cjojZmYwMDAwO2ZvbnQtc2l6ZTozMi4wcHg7Ij7kvaDlpb3kuJbnlYw8L3NwYW4+PC9kaXY+PC9k aXY+ ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106--
”aGVsbG8gd29ybGTkvaDlpb3kuJbnlYw=" base64前明文:
hello world你好世界
"
PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij5oZWxsbyB3b3JsZDwvc3Bhbj48 L2Rpdj48ZGl2ICBzdHlsZT0iY2xlYXI6Ym90aDsiPjxzcGFuICBzdHlsZT0iZm9udC1mYW1pbHk6 VGFob21hLEFyaWFsLFNUSGVpdGksU2ltU3VuO2NvbG9yOiMwMDAwMDA7YmFja2dyb3VuZC1jb2xv cjojZmYwMDAwO2ZvbnQtc2l6ZTozMi4wcHg7Ij7kvaDlpb3kuJbnlYw8L3NwYW4+PC9kaXY+PC9k aXY+" 明文:
<div class="__aliyun_email_body_block"><div style="clear:both;"><span style="font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;color:#000000;">hello world</span></div>
<div style="clear:both;"><span style="font-family:Tahoma,Arial,STHeiti,SimSun;color:#000000;background-color:#ff0000;font-size:32.0px;">你好世界</span></div></div>
3) 將圖片內嵌:
Content-Type: multipart/related; boundary="----=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d" ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d Content-Type: multipart/alternative; boundary="----=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e" ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: base64 6L+Z5pivcHl0aG9u55qEbG9nb8KgCg== ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: base64 PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7ov5nmmK9weXRob27nmoRsb2dv PC9zcGFuPjwvZGl2PjxkaXYgIHN0eWxlPSJjbGVhcjpib3RoOyI+PHNwYW4gIHN0eWxlPSJmb250 LWZhbWlseTpUYWhvbWEsQXJpYWwsU1RIZWl0aSxTaW1TdW47Zm9udC1zaXplOjE0LjBweDtjb2xv cjojMDAwMDAwOyI+PGltZyAgc3JjPSJjaWQ6X19hbGl5dW4xNTIwMDQyNzk4MjEzOTg3MjEiPiZu YnNwOzxiciA+PC9zcGFuPjwvZGl2PjwvZGl2Pg== ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e-- ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d Content-Type: application/octet-stream Content-ID: <__aliyun152004279821398721> Content-Disposition: inline; filename="=?UTF-8?B?cHl0aG9uLWxvZ28ucG5n?=" Content-Transfer-Encoding: base64 下面多行是圖片二進制數據base64編碼后的數據 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d--
"6L+Z5pivcHl0aG9u55qEbG9nb8KgCg==" 是經過base64編碼后的數據,明文為:
這是python的logo
"
PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7ov5nmmK9weXRob27nmoRsb2dv
PC9zcGFuPjwvZGl2PjxkaXYgIHN0eWxlPSJjbGVhcjpib3RoOyI+PHNwYW4gIHN0eWxlPSJmb250
LWZhbWlseTpUYWhvbWEsQXJpYWwsU1RIZWl0aSxTaW1TdW47Zm9udC1zaXplOjE0LjBweDtjb2xv
cjojMDAwMDAwOyI+PGltZyAgc3JjPSJjaWQ6X19hbGl5dW4xNTIwMDQyNzk4MjEzOTg3MjEiPiZu
YnNwOzxiciA+PC9zcGFuPjwvZGl2PjwvZGl2Pg==" 明文為:
<div class="__aliyun_email_body_block"><div style="clear:both;"><span style="font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;color:#000000;">
這是python的logo</span></div><div style="clear:both;"><span style="font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14.0px;color:#000000;">
<img src="cid:__aliyun152004279821398721"> <br ></span></div></div>
4) 發送帶附件的郵件
Content-Type: multipart/mixed; boundary="----=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709" This is a multi-part message in MIME format. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709 Content-Type: multipart/alternative; boundary="----=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a" ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: base64 5Y+R6YCB6ZmE5Lu25rWL6K+V ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: base64 PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7lj5HpgIHpmYTku7Y8c3BhbiAg c3R5bGU9ImNvbG9yOiMwMDAwMDA7Zm9udC1mYW1pbHk6VGFob21hLEFyaWFsLFNUSGVpdGksU2lt U3VuO2ZvbnQtc2l6ZToxNC4wcHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50LWxpZ2F0 dXJlczpub3JtYWw7Zm9udC12YXJpYW50LWNhcHM6bm9ybWFsO2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0 LWFsaWduOnN0YXJ0O3RleHQtaW5kZW50Oi4wcHg7dGV4dC10cmFuc2Zvcm06bm9uZTt3aWRvd3M6 MjtiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmY7dGV4dC1kZWNvcmF0aW9uLXN0eWxlOmluaXRpYWw7 dGV4dC1kZWNvcmF0aW9uLWNvbG9yOmluaXRpYWw7ZmxvYXQ6bm9uZTtkaXNwbGF5OmlubGluZTsi Pua1i+ivlTwvc3Bhbj48L3NwYW4+PC9kaXY+PC9kaXY+ ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a-- ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709 Content-Type: application/octet-stream Content-Disposition: attachment; filename="=?UTF-8?B?5L2g5aW9d29ybGQudHh0?=" Content-Transfer-Encoding: base64 aGVsbG8gd29ybGQNCsTjusPKwL3n ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709--
multipart/alternative:
在同時提供多種消息格式,這些消息格式間是相互可替換的,如提供同一消息的多語言版本。
multipart/related:
添加內嵌資源時必須指定的Content-Type類型,通常配合Content-ID使用。
Content-ID頭字段用於為“multipart/related” 組合消息中的內嵌資源指定一個唯一的標識符。在html格式的正文中使用這個唯一標識號來引用該內嵌資源。格式如下:
<img src="cid:CONTENTID" /> # CONTENTID用具體的數值替換
multipart/mixed:
添加附件時必須指定的Content-Type類型, 配合Content-Disposition來獲取上文文件的名稱。
Content-Disposition: attachment; filename=”上傳文件的文件名" # 一般中文文件名都需要base64編碼 # 以python_logo.png為例,base64編碼后變為: cHl0aG9uX2xvZ28ucG5n # 最后結果為: =?UTF-8?B?cHl0aG9uLWxvZ28ucG5n?=
3. python中的MIME
參考: email.mime

MIMENonMultipart 作為中間類,主要用途是防止調用attach()方法
MIMEMultipart 與Conent-Type中的multipart對應,默認類型為multipart/mixed
MIMEApplication與Content-Type中的application對應,默認類型為application/octed-stream,默認編碼類型為base64
MIMEText與Content-Type中的text對應,默認類型為text/plain。
身份認證發生錯誤:
smtplib.SMTPAuthenticationError: (535, 'Error: authentication failed')
原因分析:網易163郵箱需要啟用授權碼,並且使用授權碼登錄,而不是密碼登錄

1. 發送純文本
import smtplib from email.mime.text import MIMEText from email.header import Header host = "smtp.aliyun.com" username = "u1@aliyun.com" # 對於163郵箱使用授權碼而不是密碼登錄 password = "xxxxxxxxxxxx" # 接收人列表 receivers = ['q1@qq.com'] # 抄送人列表 cc_list = ['q2@qq.com'] # 密送人列表 bcc_list = ['q3@qq.com'] # 主題 subject = "阿里郵箱給qq郵箱發信" # 正文 body = "Python 發送郵件測試" sender = username # 郵件正文 message = MIMEText(body, 'plain', 'utf-8') # 發件人 message['From'] = sender # 收件人 message['To'] = ",".join(receivers) # 多個接收人之間要用逗號隔開 # 抄送人 message['Cc'] = ','.join(cc_list) # 密送人 message['Bcc'] = ','.join(bcc_list) # 主題 message['Subject'] = Header(subject, "utf-8") try: smtp = smtplib.SMTP() smtp.set_debuglevel(1) # 開啟調試,方便觀察和郵件服務器通信全過程 smtp.connect(host) # 默認端口是25 # 登陸 smtp.login(username, password) # 發送郵件 # 郵件服務器在轉發郵件的過程中,省略了Bcc項 # To: Cc: Bcc: 只是一種表現形式,歸根結底都是郵件的接收者,都需要服務器發送RCPT TO:來告知郵件服務器發送給誰。 # 因此接收者列表應該是收件人、抄送人、密送人的集合。 smtp.sendmail(sender, receivers + cc_list + bcc_list, message.as_string()) smtp.quit() except smtplib.SMTPException as e: print("error: ", e)
案例: 將磁盤使用情況以郵件的形式發送
# coding=utf-8 import smtplib import subprocess def send_email(smtp_server, sender, passwd, receiver_list, subject, content): receiver = receiver_list if isinstance(receiver_list, list): receiver = ",".join(receiver_list) # 多個收件人之間用,隔開 # 注意主題和正文中有一個空行 msg = '\r\n'.join([ 'From: %s' % sender, 'To: %s' % receiver, 'Subject: %s' % subject, '', content ]) try: s = smtplib.SMTP() # 連接smtp服務器 s.connect(smtp_server) # 登陸 對於163郵箱,使用授權碼而不是密碼登陸第三方郵件客戶端 s.login(sender, passwd) # 發送郵件 s.sendmail(sender, receiver_list, msg) print 'success' s.quit() except Exception as e: print e # 發送郵件測試 def send_email_test(): smtp_server = 'smtp.163.com' sender = 'sender@163.com' # 發件人 password = 'authorized_code' # 163郵箱的登陸授權碼 # 收件人 receiver = ['qq1@qq.com', 'qq2@qq.com'] # 郵件主題 subject = 'Disk Usage Report' p = subprocess.Popen('df -h', shell=True, stdout=subprocess.PIPE) content = p.stdout.read() # 獲取磁盤使用情況 send_email(smtp_server, sender, password, receiver, subject, content)
2. 發送富文本郵件
host = "smtp.aliyun.com" username = "u1@aliyun.com" password = “password" receivers = ['q1@qq.com'] subject = "python發送html" sender = username # 類型為alternative message = MIMEMultipart("alternative") message['From'] = sender message['To'] = ",".join(receivers) # 多個接收人之間要用逗號隔開 message['Subject'] = Header(subject, "utf-8") # 正文有兩部分 part1 = MIMEText("<h1>使用python發送郵件</h1>", "html", "utf-8") part2 = MIMEText('<a href="http://www.python.org">python官網</a>', "html", "utf-8") message.attach(part1) message.attach(part2)
3. 發送帶附件的郵件
import os import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header from email.mime.application import MIMEApplication def add_attchment(message, file): """添加附件 :param message: MIMEMultipart對象 :param file: file為文件的路徑 :return: None """ filename = os.path.basename(file) # 獲取文件名 with open(file, "rb") as f: attachment = MIMEApplication(f.read()) attachment.add_header( "Content-Disposition", "attachment", filename=( "gbk", # 指定編碼格式, qq郵箱對郵件使用gbk編碼,否則可能會出現文件名亂碼 "", filename)) message.attach(attachment) host = "smtp.aliyun.com" username = "u1@aliyun.com" password = “password" receivers = ['q1@qq.com'] subject = "python發送附件" sender = username # 發送附件,Content-Type必須為multipart/mixed message = MIMEMultipart("mixed") message['From'] = sender message['To'] = ",".join(receivers) # 多個接收人之間要用逗號隔開 message['Subject'] = Header(subject, "utf-8") # 添加正文 body = MIMEText("測試發送附件", "html", "utf-8") message.attach(body) # 添加附件 add_attchment(message, "百度logo.jpg") add_attchment(message, "測試.txt") add_attchment(message, "測試.xlsx")
4. 發送帶內嵌圖片的郵件
def get_base64name(file): """ :param file: 文件路徑 :return: 返回base64編碼的文件名 """ filename = os.path.basename(file) base64name = base64.b64encode(filename.encode('utf-8')) filename = (str(base64name))[2:-1] return filename def add_nested_picture(message, msgalternative, file): """ 向郵件中嵌入圖片 """ # 將base64編碼的文件名作為cid cid = get_base64name(file) link = MIMEText('<img src="cid:%s" />' % cid, "html", "utf-8") msgalternative.attach(link) with open(file, "rb") as f: nested_picture = MIMEApplication(f.read()) nested_picture.add_header("Content-ID", cid) # 下面這一行可以省略 nested_picture.add_header("Content-Disposition", "inline", filename=("gbk", "", os.path.basename(file))) message.attach(nested_picture) sender = username
# 指定類型為multipart/related message = MIMEMultipart("related") message['From'] = sender message['To'] = ",".join(receivers) # 多個接收人之間要用逗號隔開 message['Subject'] = Header(subject, "utf-8") msgalternative = MIMEMultipart("alternative") message.attach(msgalternative) # 添加正文 body = MIMEText("測試發送含有內嵌圖片的郵件", "html", "utf-8") msgalternative.attach(body) add_nested_picture(message, msgalternative, "C:/users/hupeng/pictures/lena.jpg")
層次結果:
里層: 類型為multipart/alternative的MIMEMultipart對象attach 裝載html文本的MIMEText對象。
外層: 類型為multipart/related的MIMEMultipart對象 attach 表示內嵌資源的MIMEApplication對象和表示文本信息類型為mulitpart/alternative的MIMEMultipart對象。
當然也可以直接使用類型為multipart/mixed類型的對象attach 表示html文本的MIME對象和表示內嵌資源的MIMEApplication對象
代碼如下:
def get_base64name(file): """ :param file: 文件路徑 :return: 返回base64編碼的文件名 """ filename = os.path.basename(file) base64name = base64.b64encode(filename.encode('utf-8')) filename = (str(base64name))[2:-1] return filename def add_nested_picture(message, file): """ 向郵件中嵌入圖片 """ # 將base64編碼的文件名作為cid cid = get_base64name(file) link = MIMEText('<img src="cid:%s" />' % cid, "html", "utf-8") message.attach(link) with open(file, "rb") as f: nested_picture = MIMEApplication(f.read()) nested_picture.add_header("Content-ID", cid) message.attach(nested_picture) sender = username # 指定最外層信息段類型為multipart/mixed message = MIMEMultipart("mixed") message['From'] = sender message['To'] = ",".join(receivers) # 多個接收人之間要用逗號隔開 message['Subject'] = Header(subject, "utf-8") # 添加正文 body = MIMEText("測試發送含有內嵌圖片的郵件", "html", "utf-8") message.attach(body) # 添加嵌入式圖片 add_nested_picture(message, "百度logo.jpg")

備注: 郵件客戶端(web端)包含查看郵件原文的功能,使用該功能可以很好的幫助學習。
