python要实现发送邮件的功能,需要使用smtplib库。

1. 过程大致如下:

1. 建立和SMTP邮件服务器的连接

  1. # 默认端口25
  2. smtp = smtplib.SMTP(host, port)
  3. # 或者
  4. smtp = smtplib.SMTP()
  5. smtp.connect(host, port)
  6.  
  7. # 带SSL,默认端口465
  8. smtp = smtp.SMTP_SSL()
  9. smtp.connect(host, port)

2. 完成身份认证

  1. # 对于163而言,第三方客户端登陆使用的是授权码而不是密码
  2. smtp.login(username, password)

3. 发送邮件(必须明确发件人、收件人、主题、内容,而抄送人、密送人、附件、内嵌式图片等资源是可选的)

  1. smtp.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])

4. 结束会话

  1. smtp.quit()

2. MIME相关知识介绍

MIME全称Multipurpose Internet Mail Extensions(多用途互联网邮件扩展)。是一个互联网标准,它扩展了电子邮件标准,使其能够支持:

  • 非ASCII字符文本;
  • 非文本格式附件(二进制、声音、图像等);
  • 由多部分(multiple parts)组成的消息体;
  • 包含非ASCII字符的头信息(Header information)

此外,web中使用的HTTP协议也使用了MIMIE的框架,标准被扩展为互联网媒体类型

MIME头部信息:

MIME版本: 目前版本1.0

  1. MIME-Version: 1.0

内容类型(Content-Type),用于指定消息类型。 形式如下:

  1. 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
.pdf 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)

  1. 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) 发送纯文本文件:

  1. Content-Type: text/plain; charset="UTF-8"
  2. Content-Transfer-Encoding: base64
  3.  
  4. aGVsbG8gd29ybGQK5L2g5aW95LiW55WM

2)发送包含富文本的邮件:

  1. Content-Type: multipart/alternative;
  2. boundary="----=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106"
  3.  
  4. ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106
  5. Content-Type: text/plain; charset="UTF-8"
  6. Content-Transfer-Encoding: base64
  7.  
  8. aGVsbG8gd29ybGTkvaDlpb3kuJbnlYw=
  9. ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106
  10. Content-Type: text/html; charset="UTF-8"
  11. Content-Transfer-Encoding: base64
  12.  
  13. PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
  14. OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
  15. bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij5oZWxsbyB3b3JsZDwvc3Bhbj48
  16. L2Rpdj48ZGl2ICBzdHlsZT0iY2xlYXI6Ym90aDsiPjxzcGFuICBzdHlsZT0iZm9udC1mYW1pbHk6
  17. VGFob21hLEFyaWFsLFNUSGVpdGksU2ltU3VuO2NvbG9yOiMwMDAwMDA7YmFja2dyb3VuZC1jb2xv
  18. cjojZmYwMDAwO2ZvbnQtc2l6ZTozMi4wcHg7Ij7kvaDlpb3kuJbnlYw8L3NwYW4+PC9kaXY+PC9k
  19. aXY+
  20. ------=ALIBOUNDARY_14343_4f941940_5a9a0b10_90106--

”aGVsbG8gd29ybGTkvaDlpb3kuJbnlYw=" base64前明文:

  1. hello world你好世界

"

  1. PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
  2. OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
  3. bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij5oZWxsbyB3b3JsZDwvc3Bhbj48
  4. L2Rpdj48ZGl2ICBzdHlsZT0iY2xlYXI6Ym90aDsiPjxzcGFuICBzdHlsZT0iZm9udC1mYW1pbHk6
  5. VGFob21hLEFyaWFsLFNUSGVpdGksU2ltU3VuO2NvbG9yOiMwMDAwMDA7YmFja2dyb3VuZC1jb2xv
  6. cjojZmYwMDAwO2ZvbnQtc2l6ZTozMi4wcHg7Ij7kvaDlpb3kuJbnlYw8L3NwYW4+PC9kaXY+PC9k
  7. aXY+" 明文:
  1. <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>
  1.  

3) 将图片内嵌:

  1. Content-Type: multipart/related;
  2. boundary="----=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d"
  3.  
  4. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d
  5. Content-Type: multipart/alternative;
  6. boundary="----=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e"
  7.  
  8. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e
  9. Content-Type: text/plain; charset="UTF-8"
  10. Content-Transfer-Encoding: base64
  11.  
  12. 6L+Z5pivcHl0aG9u55qEbG9nb8KgCg==  
  13. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e
  14. Content-Type: text/html; charset="UTF-8"
  15. Content-Transfer-Encoding: base64
  16.  
  17. PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
  18. OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
  19. bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7ov5nmmK9weXRob27nmoRsb2dv
  20. PC9zcGFuPjwvZGl2PjxkaXYgIHN0eWxlPSJjbGVhcjpib3RoOyI+PHNwYW4gIHN0eWxlPSJmb250
  21. LWZhbWlseTpUYWhvbWEsQXJpYWwsU1RIZWl0aSxTaW1TdW47Zm9udC1zaXplOjE0LjBweDtjb2xv
  22. cjojMDAwMDAwOyI+PGltZyAgc3JjPSJjaWQ6X19hbGl5dW4xNTIwMDQyNzk4MjEzOTg3MjEiPiZu
  23. YnNwOzxiciA+PC9zcGFuPjwvZGl2PjwvZGl2Pg==
  24. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9e--
  25.  
  26. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d
  27. Content-Type: application/octet-stream
  28. Content-ID: <__aliyun152004279821398721>
  29. Content-Disposition: inline; filename="=?UTF-8?B?cHl0aG9uLWxvZ28ucG5n?="
  30. Content-Transfer-Encoding: base64
  31.  
  32. 下面多行是图片二进制数据base64编码后的数据
  33. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  34.  
  35. ------=ALIBOUNDARY_25467_4f851940_5a9a032e_8ff9d--

"6L+Z5pivcHl0aG9u55qEbG9nb8KgCg==" 是经过base64编码后的数据,明文为:

  1. 这是pythonlogo
  1. "

PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7ov5nmmK9weXRob27nmoRsb2dv
PC9zcGFuPjwvZGl2PjxkaXYgIHN0eWxlPSJjbGVhcjpib3RoOyI+PHNwYW4gIHN0eWxlPSJmb250
LWZhbWlseTpUYWhvbWEsQXJpYWwsU1RIZWl0aSxTaW1TdW47Zm9udC1zaXplOjE0LjBweDtjb2xv
cjojMDAwMDAwOyI+PGltZyAgc3JjPSJjaWQ6X19hbGl5dW4xNTIwMDQyNzk4MjEzOTg3MjEiPiZu
YnNwOzxiciA+PC9zcGFuPjwvZGl2PjwvZGl2Pg==" 明文为:

  1. <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">&nbsp;<br ></span></div></div>

4) 发送带附件的邮件

  1. Content-Type: multipart/mixed;
  2. boundary="----=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709"
  3.  
  4. This is a multi-part message in MIME format.
  5.  
  6. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709
  7. Content-Type: multipart/alternative;
  8. boundary="----=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a"
  9.  
  10. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a
  11. Content-Type: text/plain; charset="UTF-8"
  12. Content-Transfer-Encoding: base64
  13.  
  14. 5Y+R6YCB6ZmE5Lu25rWL6K+V
  15. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a
  16. Content-Type: text/html; charset="UTF-8"
  17. Content-Transfer-Encoding: base64
  18.  
  19. PGRpdiBjbGFzcz0iX19hbGl5dW5fZW1haWxfYm9keV9ibG9jayI+PGRpdiAgc3R5bGU9ImNsZWFy
  20. OmJvdGg7Ij48c3BhbiAgc3R5bGU9ImZvbnQtZmFtaWx5OlRhaG9tYSxBcmlhbCxTVEhlaXRpLFNp
  21. bVN1bjtmb250LXNpemU6MTQuMHB4O2NvbG9yOiMwMDAwMDA7Ij7lj5HpgIHpmYTku7Y8c3BhbiAg
  22. c3R5bGU9ImNvbG9yOiMwMDAwMDA7Zm9udC1mYW1pbHk6VGFob21hLEFyaWFsLFNUSGVpdGksU2lt
  23. U3VuO2ZvbnQtc2l6ZToxNC4wcHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50LWxpZ2F0
  24. dXJlczpub3JtYWw7Zm9udC12YXJpYW50LWNhcHM6bm9ybWFsO2ZvbnQtd2VpZ2h0OjQwMDt0ZXh0
  25. LWFsaWduOnN0YXJ0O3RleHQtaW5kZW50Oi4wcHg7dGV4dC10cmFuc2Zvcm06bm9uZTt3aWRvd3M6
  26. MjtiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmY7dGV4dC1kZWNvcmF0aW9uLXN0eWxlOmluaXRpYWw7
  27. dGV4dC1kZWNvcmF0aW9uLWNvbG9yOmluaXRpYWw7ZmxvYXQ6bm9uZTtkaXNwbGF5OmlubGluZTsi
  28. Pua1i+ivlTwvc3Bhbj48L3NwYW4+PC9kaXY+PC9kaXY+
  29. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_9070a--
  30.  
  31. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709
  32. Content-Type: application/octet-stream
  33. Content-Disposition: attachment; filename="=?UTF-8?B?5L2g5aW9d29ybGQudHh0?="
  34. Content-Transfer-Encoding: base64
  35.  
  36. aGVsbG8gd29ybGQNCsTjusPKwL3n
  37.  
  38. ------=ALIBOUNDARY_68661_4cbc5940_5a9a0f2a_90709--

multipart/alternative:

在同时提供多种消息格式,这些消息格式间是相互可替换的,如提供同一消息的多语言版本。

multipart/related:

添加内嵌资源时必须指定的Content-Type类型,通常配合Content-ID使用。

Content-ID头字段用于为“multipart/related” 组合消息中的内嵌资源指定一个唯一的标识符。在html格式的正文中使用这个唯一标识号来引用该内嵌资源。格式如下:

  1. <img src="cid:CONTENTID" />
  2. # CONTENTID用具体的数值替换

multipart/mixed:

  添加附件时必须指定的Content-Type类型, 配合Content-Disposition来获取上文文件的名称。

  1. Content-Disposition: attachment; filename=”上传文件的文件名"
  2. # 一般中文文件名都需要base64编码
  3. # 以python_logo.png为例,base64编码后变为: cHl0aG9uX2xvZ28ucG5n
  4. # 最后结果为: =?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。

身份认证发生错误:

  1. smtplib.SMTPAuthenticationError: (535, 'Error: authentication failed')

原因分析:网易163邮箱需要启用授权码,并且使用授权码登录,而不是密码登录

1. 发送纯文本

  1. import smtplib
  2. from email.mime.text import MIMEText
  3. from email.header import Header
  4.  
  5. host = "smtp.aliyun.com"
  6. username = "u1@aliyun.com"
  7. # 对于163邮箱使用授权码而不是密码登录
  8. password = "xxxxxxxxxxxx"
  9.  
  10. # 接收人列表
  11. receivers = ['q1@qq.com']
  12. # 抄送人列表
  13. cc_list = ['q2@qq.com']
  14. # 密送人列表
  15. bcc_list = ['q3@qq.com']
  16. # 主题
  17. subject = "阿里邮箱给qq邮箱发信"
  18. # 正文
  19. body = "Python 发送邮件测试"
  20.  
  21. sender = username
  22. # 邮件正文
  23. message = MIMEText(body, 'plain', 'utf-8')
  24. # 发件人
  25. message['From'] = sender
  26. # 收件人
  27. message['To'] = ",".join(receivers) # 多个接收人之间要用逗号隔开
  28. # 抄送人
  29. message['Cc'] = ','.join(cc_list)
  30. # 密送人
  31. message['Bcc'] = ','.join(bcc_list)
  32. # 主题
  33. message['Subject'] = Header(subject, "utf-8")
  34.  
  35. try:
  36. smtp = smtplib.SMTP()
  37. smtp.set_debuglevel(1) # 开启调试,方便观察和邮件服务器通信全过程
  38. smtp.connect(host) # 默认端口是25
  39. # 登陆
  40. smtp.login(username, password)
  41. # 发送邮件
  42. # 邮件服务器在转发邮件的过程中,省略了Bcc项
  43. # To: Cc: Bcc: 只是一种表现形式,归根结底都是邮件的接收者,都需要服务器发送RCPT TO:来告知邮件服务器发送给谁。
  44. # 因此接收者列表应该是收件人、抄送人、密送人的集合。
  45. smtp.sendmail(sender, receivers + cc_list + bcc_list, message.as_string())
  46. smtp.quit()
  47. except smtplib.SMTPException as e:
  48. print("error: ", e)

案例: 将磁盘使用情况以邮件的形式发送

  1. # coding=utf-8
  2. import smtplib
  3. import subprocess
  4.  
  5. def send_email(smtp_server, sender, passwd, receiver_list, subject, content):
  6. receiver = receiver_list
  7. if isinstance(receiver_list, list):
  8. receiver = ",".join(receiver_list) # 多个收件人之间用,隔开
  9.  
  10. # 注意主题和正文中有一个空行
  11. msg = '\r\n'.join([
  12. 'From: %s' % sender,
  13. 'To: %s' % receiver,
  14. 'Subject: %s' % subject,
  15. '',
  16. content
  17. ])
  18.  
  19. try:
  20. s = smtplib.SMTP()
  21. # 连接smtp服务器
  22. s.connect(smtp_server)
  23. # 登陆 对于163邮箱,使用授权码而不是密码登陆第三方邮件客户端
  24. s.login(sender, passwd)
  25. # 发送邮件
  26. s.sendmail(sender, receiver_list, msg)
  27. print 'success'
  28. s.quit()
  29. except Exception as e:
  30. print e
  31.  
  32. # 发送邮件测试
  33.  
  34. def send_email_test():
  35. smtp_server = 'smtp.163.com'
  36. sender = 'sender@163.com' # 发件人
  37. password = 'authorized_code' # 163邮箱的登陆授权码
  38.  
  39. # 收件人
  40. receiver = ['qq1@qq.com', 'qq2@qq.com']
  41. # 邮件主题
  42. subject = 'Disk Usage Report'
  43.  
  44. p = subprocess.Popen('df -h', shell=True, stdout=subprocess.PIPE)
  45. content = p.stdout.read() # 获取磁盘使用情况
  46.  
  47. send_email(smtp_server, sender, password, receiver, subject, content)

2. 发送富文本邮件

  1. host = "smtp.aliyun.com"
  2. username = "u1@aliyun.com"
  3. password = password"
  4. receivers = ['q1@qq.com']
  5. subject = "python发送html"
  6.  
  7. sender = username
  8. # 类型为alternative
  9. message = MIMEMultipart("alternative")
  10. message['From'] = sender
  11. message['To'] = ",".join(receivers) # 多个接收人之间要用逗号隔开
  12. message['Subject'] = Header(subject, "utf-8")
  13.  
  14. # 正文有两部分
  15. part1 = MIMEText("<h1>使用python发送邮件</h1>", "html", "utf-8")
  16. part2 = MIMEText('<a href="http://www.python.org">python官网</a>', "html", "utf-8")
  17. message.attach(part1)
  18. message.attach(part2)

3. 发送带附件的邮件

  1. import os
  2. import smtplib
  3. from email.mime.text import MIMEText
  4. from email.mime.multipart import MIMEMultipart
  5. from email.header import Header
  6. from email.mime.application import MIMEApplication
  7.  
  8. def add_attchment(message, file):
  9. """添加附件
  10. :param message: MIMEMultipart对象
  11. :param file: file为文件的路径
  12. :return: None
  13. """
  14. filename = os.path.basename(file) # 获取文件名
  15. with open(file, "rb") as f:
  16. attachment = MIMEApplication(f.read())
  17. attachment.add_header(
  18. "Content-Disposition",
  19. "attachment",
  20. filename=(
  21. "gbk", # 指定编码格式, qq邮箱对邮件使用gbk编码,否则可能会出现文件名乱码
  22. "",
  23. filename))
  24. message.attach(attachment)
  25.  
  26. host = "smtp.aliyun.com"
  27. username = "u1@aliyun.com"
  28. password = password"
  29. receivers = ['q1@qq.com']
  30. subject = "python发送附件"
  31.  
  32. sender = username
  33. # 发送附件,Content-Type必须为multipart/mixed
  34. message = MIMEMultipart("mixed")
  35. message['From'] = sender
  36. message['To'] = ",".join(receivers) # 多个接收人之间要用逗号隔开
  37. message['Subject'] = Header(subject, "utf-8")
  38. # 添加正文
  39. body = MIMEText("测试发送附件", "html", "utf-8")
  40. message.attach(body)
  41. # 添加附件
  42. add_attchment(message, "百度logo.jpg")
  43. add_attchment(message, "测试.txt")
  44. add_attchment(message, "测试.xlsx")

4. 发送带内嵌图片的邮件

  1. def get_base64name(file):
  2. """
  3. :param file: 文件路径
  4. :return: 返回base64编码的文件名
  5. """
  6. filename = os.path.basename(file)
  7. base64name = base64.b64encode(filename.encode('utf-8'))
  8. filename = (str(base64name))[2:-1]
  9. return filename
  10.  
  11. def add_nested_picture(message, msgalternative, file):
  12. """
  13. 向邮件中嵌入图片
  14. """
  15. # 将base64编码的文件名作为cid
  16. cid = get_base64name(file)
  17. link = MIMEText('<img src="cid:%s" />' % cid, "html", "utf-8")
  18. msgalternative.attach(link)
  19.  
  20. with open(file, "rb") as f:
  21. nested_picture = MIMEApplication(f.read())
  22. nested_picture.add_header("Content-ID", cid)
  23. # 下面这一行可以省略
  24. nested_picture.add_header("Content-Disposition", "inline", filename=("gbk", "", os.path.basename(file)))
  25. message.attach(nested_picture)
  26.  
  27. sender = username
    # 指定类型为multipart/related
  28. message = MIMEMultipart("related")
  29. message['From'] = sender
  30. message['To'] = ",".join(receivers) # 多个接收人之间要用逗号隔开
  31. message['Subject'] = Header(subject, "utf-8")
  32. msgalternative = MIMEMultipart("alternative")
  33. message.attach(msgalternative)
  34. # 添加正文
  35. body = MIMEText("测试发送含有内嵌图片的邮件", "html", "utf-8")
  36. msgalternative.attach(body)
  37. 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对象

代码如下:

  1. def get_base64name(file):
  2. """
  3. :param file: 文件路径
  4. :return: 返回base64编码的文件名
  5. """
  6. filename = os.path.basename(file)
  7. base64name = base64.b64encode(filename.encode('utf-8'))
  8. filename = (str(base64name))[2:-1]
  9. return filename
  10.  
  11. def add_nested_picture(message, file):
  12. """
  13. 向邮件中嵌入图片
  14. """
  15. # 将base64编码的文件名作为cid
  16. cid = get_base64name(file)
  17. link = MIMEText('<img src="cid:%s" />' % cid, "html", "utf-8")
  18. message.attach(link)
  19.  
  20. with open(file, "rb") as f:
  21. nested_picture = MIMEApplication(f.read())
  22. nested_picture.add_header("Content-ID", cid)
  23. message.attach(nested_picture)
  24.  
  25. sender = username
  26. # 指定最外层信息段类型为multipart/mixed
  27. message = MIMEMultipart("mixed")
  28. message['From'] = sender
  29. message['To'] = ",".join(receivers) # 多个接收人之间要用逗号隔开
  30. message['Subject'] = Header(subject, "utf-8")
  31. # 添加正文
  32. body = MIMEText("测试发送含有内嵌图片的邮件", "html", "utf-8")
  33. message.attach(body)
  34. # 添加嵌入式图片
  35. add_nested_picture(message, "百度logo.jpg")

备注:  邮件客户端(web端)包含查看邮件原文的功能,使用该功能可以很好的帮助学习。

python使用smtplib发送邮件的更多相关文章

  1. python:利用smtplib发送邮件详解

    本文转自:https://www.cnblogs.com/insane-Mr-Li/p/9121619.html 自动化测试中,测试报告一般都需要发送给相关的人员,比较有效的一个方法是每次执行完测试用 ...

  2. Python用smtplib发送邮件

    参照了下面: 1. 先随便照着试试这个: http://blog.csdn.net/zhaoweikid/article/details/1638349 2. 这个写了一个很简洁的代码,看过NO.1就 ...

  3. python smtplib发送邮件遇到的认证问题

    python的smtplib模块主要是用来发送邮件的,使用起来比较方便. 使用程序发送邮件只需要写以下几行代码就OK了: #!/usr/bin/env python import smtplib s ...

  4. python email ==> send 发送邮件 :) [smtplib, email 模块]

    关于Email的预备知识: 原贴地址:http://www.cnblogs.com/lonelycatcher/archive/2012/02/09/2343480.html ############ ...

  5. python smtplib 发送邮件简单介绍

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式python的smtplib提供了一种很 ...

  6. python ftplib,smtplib,poplib学习

    一.ftplib from ftplib import FTP ftpobj = FTP(IP或域名)     #实例化对象 ftpobj.login('username','passwd')  ## ...

  7. python简单的发送邮件

    python 利用smtplib来发送邮件,具体的代码如下 一. 编辑smtp_v2.py vim /home/python/smtp_v2.py #!/usr/bin/env python # -* ...

  8. Python使用SMTP发送邮件[HTML格式、送带附件]

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式. python的smtplib提供了一 ...

  9. python通过SMTP发送邮件失败,报错505/535

    python通过SMTP发送邮件失败:错误1:smtplib.SMTPAuthenticationError: (550, b'User has no permission')    我们使用pyth ...

随机推荐

  1. MySQL 存储过程中执行DDL

    一.定期增加表分区 1.增加表分区例 CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `p_create_Partition`(IN databaseName ...

  2. mysql的字符拼接

    在Mysql 数据库中存在两种字符串连接操作.具体操作如下 一. 语法: 1. CONCAT(string1,string2,…)   说明 : string1,string2代表字符串,concat ...

  3. 解决Ubuntu下adb无法联接手机终端

    1.首先确认开发者选项中USB调试是否打开. 2.在终端输入lsusb,如下图: 查看设备是否已经连接,如果没有此选项,请检查你的手机数据线是否正常连接,否则你的手机就可以用来防身和砸核桃了. 3.在 ...

  4. Apache Tomcat/6.0.39如何配置连接mysql,JDBC:mysql-connector-java-5.1.30-bin.jar-成功连接心得

    http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html 前提:开启TOMCAT,MYsql MySQL ...

  5. nwjs 实现的 分块上传

    1.先上核心工具类/** * Created by Administrator on 2017/6/12. */let fs = require("fs");/** * Creat ...

  6. Vue2.0 分页插件pagination使用详细说明

    Vue2.0 分页pagination使用 插件下载地址:Vue_Pagination 插件描述:基于jQuery的分页插件大家都用过很多了吧,今天分享一下基于Vue的分页插件pagination.j ...

  7. C语言SQLite3基本操作Demo

    /************************************************************************** * C语言SQLite3基本操作Demo * 声 ...

  8. for-auto使用

    前言 c++11新增了一个工具,让编译器能够根据初始值的类型推断变量的类型: c++11还新增了一种循环,基于范围的for循环,可以对数组或者容器类的每一个元素执行相同的操作:同时,可以使用& ...

  9. Plotly绘图工具(多用于统计)

    作者:桂. 时间:2017-04-23  23:52:14 链接:http://www.cnblogs.com/xingshansi/p/6754769.html 前言 无意中考到一个小工具,网址为: ...

  10. jsp内置对象request 和response

    1.request对象主要用于处理客户端的请求 request对象常用方法 一.String  request.getParameter(String name)             根据页面表单 ...