Python基础知识(34):电子邮件(Ⅰ)
几乎所有的编程语言都支持发送和接收电子邮件
在使用Python收发邮件前,请先准备好至少两个电子邮件,如xxx@163.com
,xxx@sina.com
,xxx@qq.com
等,注意两个邮箱不要用同一家邮件服务商
电子邮件发送过程:
发件人email->MUA(mail user agent:邮件用户代理)->MTA(mail transfer agent:邮件传输代理)->MDA(mail delivery agent:邮件投递代理,即服务器)<-MUA<-收件人
使用SMTP发送邮件
SMTP是发送邮件的协议,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮件
Python对SMTP支持有smtplib
和email
两个模块,email
负责构造邮件,smtplib
负责发送邮件
注:以下所有代码本人未进行测试,故无法确定是否能够正常运行
一、发送邮件
1、构造邮件
from email.mime.text import MIMEText
msg = MIMEText('Hello, world', 'plain', 'utf-8')
到构造MIMEText
对象时,第一个参数就是邮件正文,第二个参数是MIME的subtype,传入'plain'
表示纯文本,最终的MIME就是'text/plain'
,最后一定要用utf-8
编码保证多语言兼容性
2、通过SMTP发送邮件
SMTP.sendmail(from_addr, [to_addr], msg.as_string())
sendmail()
方法就是发邮件,由于可以一次发给多个人,所以传入一个list
,邮件正文是一个str
,as_string()
把MIMEText
对象变成str
必须把From
、To
和Subject
添加到MIMEText
中,才是一封完整的邮件
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplib
# 考虑到编码的原因,这里统一将name属性值改成utf-8,地址的话一定是统一的邮箱地址结构,所以不考虑
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 world!', 'plain', 'utf-8')
# 定义发送人,接收人,以及描述信息(主题)
msg['From'] = __format_addr('发件人 <%s>' % from_addr)
msg['To'] = __format_addr('收件人: <%s>' % to_addr)
msg['Subject'] = Header('SMTP......', 'utf-8').encode()
print(msg)
结果如下
From:xxx@163.com
Password:1234
To: xxxxx@qq.com
SMTP server: smtp.163.com
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
From: =?utf-8?b?5Y+R5Lu25Lq6?= <xxx@163.com>
To: xxxxx@qq.com
Subject: =?utf-8?b?U01UUC4uLi4uLg==?=
SGVsbG8gd29ybGQh
二、发送HTML邮件
在构造MIMEText
对象时,把HTML字符串传进去,再把第二个参数由plain
变为html
msg = MIMEText('<html><body><h1>Hello</h1>' +
'<p>send by <a href="http://www.python.org">Python</a>...</p>' +
'</body></html>', 'html', 'utf-8')
三、发送附件
带附件的邮件可以看做包含若干部分的邮件:文本和各个附件本身,所以,可以构造一个MIMEMultipart
对象代表邮件本身,然后往里面加上一个MIMEText
作为邮件正文,再继续往里面加上表示附件的MIMEBase
对象即可
# 邮件对象:
msg = MIMEMultipart()
msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr)
msg['To'] = _format_addr('管理员 <%s>' % to_addr)
msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode()
# 邮件正文是MIMEText:
msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))
# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open('/Users/michael/Downloads/test.png', 'rb') as f:
# 设置附件的MIME和文件名,这里是png类型:
mime = MIMEBase('image', 'png', filename='test.png')
# 加上必要的头信息:
mime.add_header('Content-Disposition', 'attachment', filename='test.png')
mime.add_header('Content-ID', '<0>')
mime.add_header('X-Attachment-Id', '0')
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码:
encoders.encode_base64(mime)
# 添加到MIMEMultipart:
msg.attach(mime)
按正常发送流程把msg
(注意类型已变为MIMEMultipart
)发送出去
四、发送图片
不能直接在HTML邮件中链接图片地址,因为大部分邮件服务商都会自动屏蔽带有外链的图片,因为不知道这些链接是否指向恶意网站
要把图片嵌入到邮件正文中,只需按照发送附件的方式,先把邮件作为附件添加进去,然后,在HTML中通过引用src="cid:0"
就可以把附件作为图片嵌入了。如果有多个图片,给它们依次编号,然后引用不同的cid:x
即可
把上面代码加入MIMEMultipart
的MIMEText
从plain
改为html
,然后在适当的位置引用图片
msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
'<p><img src="cid:0"></p>' +
'</body></html>', 'html', 'utf-8'))
五、同时支持HTML和Plain格式
如果收件人使用的设备太古老,查看不了HTML邮件,必须在发送HTML的同时再附加一个纯文本,如果收件人无法查看HTML格式的邮件,就可以自动降级查看纯文本邮件
利用MIMEMultipart
就可以组合一个HTML和Plain,要注意指定subtype是alternative
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'))
# 正常发送msg对象...
六、加密SMTP
想要更安全地发送邮件,防止发送的邮件被窃听,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件
只需要在创建SMTP
对象后,立刻调用starttls()
方法,就创建了安全连接
smtp_server = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代码和前面的一模一样:
server.set_debuglevel(1)