Table of Contents
前言
邮件发送到接收流程:
发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人
MUA:Mail User Agent——邮件用户代理 如outlook
MTA:Mail Transfer Agent——邮件传输代理 outlook等软件会发给MTA
MDA:Mail Delivery Agent——邮件投递代理 邮件内容保存在MDA
当收件上登陆邮箱时会从 MDA 获取邮件内容
发邮件时,MUA和MTA使用的协议就是SMTP:
Simple Mail Transfer Protocol,后面的MTA到另一个MTA也是用SMTP协议。
收邮件时,MUA和MDA使用的协议有两种:
POP:Post Office Protocol,目前版本是3,俗称POP3;
IMAP:Internet Message Access Protocol,目前版本是4。
SMPT发送邮件
简单案例
使用如下
from email.mime.text import MIMEText # 负责构建邮件
# 参数一:邮件正文
# 参数二:邮件内容类型,plain代表纯文本
msg = MIMEText('Hello,send by Python..', 'plain', 'utf-8')
# 输入Email地址和口令
from_addr = input('From:')
password = input('Password:')
# 输入收件人地址:
to_addr = input('To:')
# 输入SMIP服务器地址:
smtp_server = input('SMTP server:') # 形如 smtp.qq.com 这是qq邮箱的smtp服务器地址
import smtplib # 负责发送邮件
# 连接SMTP服务器
server = smtplib.SMTP(smtp_server, 25) # SMTP 协议默认端口是25
# server = smtplib.SMTP_SSL(smtp_server, 465) 使用SSL协议的话 端口号就是465,SSL能够对传输内容以及用户名密码进行加密
# 使其可以打印出和SMTP服务器交互的所有信息,默认参数为false
server.set_debuglevel(1)
# 输入用户名密码登陆SMTP
server.login(from_addr, password)
# 发送邮件
# 参数一 发件人
# 参数二 收件人 可以有多个,是个list
# 参数三 邮件内容
server.sendmail(from_addr, [to_addr], msg.as_string()) # as_string将MIMEText转换成str
server.quit()
我是用qq邮箱向163邮箱发送的邮件,其中qq邮箱的密码不是我们登陆的密码,而是邮箱设置中的授权码。
收到的邮件如下:
注意如果使用了SSL,会出现在垃圾邮件中...并且无主题
没使用SSL 就可以在收件箱中看到
通过上面的图可以看出,邮件的 主题 均为 无主题,而发件人是我的qq号(我给抹了)。如果想更改主题和收件人以及发件人的内容,我们需要更改MIMEText对象的一些内容,如下:
设置主题、收件人、发件人名称
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplib
def _format_addr(s):
name, addr = parseaddr(s) # 将字符串拆分开为一个元组包含两个元素
# 前一部分收件人和发件人的姓名,后一部分是地址
# Header是MIME 头信息 对象,类似于Request Header,encode函数将Header对象转换成RFC格式
# formataddr 和 parseaddr 作用相反
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('自己 <%s>' % from_addr)
msg['To'] = _format_addr('张子豪 <%s>' % to_addr)
msg['Subject'] = Header('SMTP测试……', 'utf-8').encode()
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password) # 与SMTP服务器连接
server.sendmail(from_addr, [to_addr], msg.as_string()) # 发送信息
server.quit()
效果如下
上面所提到的RFC格式就类似下面这样:
From: =?utf-8?b?UHl0aG9u54ix5aW96ICF?= <[email protected]>
To: =?utf-8?b?566h55CG5ZGY?= <[email protected]>
Subject: =?utf-8?b?5p2l6IeqU01UUOeahOmXruWAmeKApuKApg==?=
发送HTML邮件
就是邮件的内容是HTML形式的
使用方式如下:
msg = MIMEText('<html><body><h1>Hello</h1>' + # 发送的内容是html元素
'<p>send by <a href="http://www.python.org">张子豪</a>...</p>' +
'</body></html>', 'html', 'utf-8') # 将第二个参数改为html
发送附件 以及图片
如果Email中要加上附件怎么办?带附件的邮件可以看做包含若干部分的邮件:文本和各个附件本身,所以,可以构造一个MIMEMultipart
对象代表邮件本身,然后往里面加上一个MIMEText
作为邮件正文,再继续往里面加上表示附件的MIMEBase
对象即可,如果想将附件的图片放入正文当中,则正文应是html形式的,然后在src标签中通过引用附件的id来获取到图片,注意以这种形式附件不会在收件人下方显示出来,而是当你鼠标放在正文中的图片上出现下载按钮。
如下:
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart, MIMEBase
from email.utils import parseaddr, formataddr
import smtplib
import requests
def _format_addr(s):
name, addr = parseaddr(s) # 将字符串拆分开为一个元组包含两个元素
# 前一部分收件人和发件人的姓名,后一部分是地址
# Header是MIME 头信息 对象,类似于Request Header,encode函数将Header对象转换成RFC格式
# formataddr 和 parseaddr 作用相反
return formataddr((Header(name, 'utf-8').encode(), addr))
# 获得本人博客页面源码
r = requests.get('https://blog.csdn.net/weixin_40247263/article/list/1')
from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')
# 邮件对象:
msg = MIMEMultipart()
msg['From'] = _format_addr('自己 <%s>' % from_addr)
msg['To'] = _format_addr('张子豪 <%s>' % to_addr)
msg['Subject'] = Header('SMTP测试……', 'utf-8').encode()
# 邮件正文是MIMEText:
msg.attach(MIMEText('<h1>Hello</h1>' +
'<p><img src="cid:0"></p>' + # 引用附件的图片,将其放入正文当中
r.text, 'html', 'utf-8'))
# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open(r'C:\Users\Administrator\Desktop\img\1.jpg', 'rb') as f:
# 设置附件的MIME和文件名,这里是jpg类型:
mime = MIMEBase('image', 'jpg', filename='test.jpg') # filename代表下载以后的文件名
# 加上必要的头信息:
mime.add_header('Content-Disposition', 'attachment', filename='test.jpg')
mime.add_header('Content-ID', '<0>') # 给附件起个id值,说白了就是唯一标识
mime.add_header('X-Attachment-Id', '0') # 这行跟网络连接有关,如果不加会报socket.gaierror
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码:
encoders.encode_base64(mime)
# 添加到MIMEMultipart:
msg.attach(mime)
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password) # 与SMTP服务器连接
server.sendmail(from_addr, [to_addr], msg.as_string()) # 发送信息
server.quit()
效果如下:
如果我们没有将附件的图片放入正文当中,效果如下:
同时支持HTML和Plain格式
如果我们发送HTML邮件,收件人通过浏览器或者Outlook之类的软件是可以正常浏览邮件内容的,但是,如果收件人使用的设备太古老,查看不了HTML邮件怎么办?
办法是在发送HTML的同时再附加一个纯文本,如果收件人无法查看HTML格式的邮件,就可以自动降级查看纯文本邮件。
利用MIMEMultipart
就可以组合一个HTML和Plain,要注意指定subtype是alternative
:
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart, MIMEBase
from email.utils import parseaddr, formataddr
import smtplib
import requests
def _format_addr(s):
name, addr = parseaddr(s) # 将字符串拆分开为一个元组包含两个元素
# 前一部分收件人和发件人的姓名,后一部分是地址
# Header是MIME 头信息 对象,类似于Request Header,encode函数将Header对象转换成RFC格式
# formataddr 和 parseaddr 作用相反
return formataddr((Header(name, 'utf-8').encode(), addr))
# 获得本人博客页面源码
r = requests.get('https://blog.csdn.net/weixin_40247263/article/list/1')
from_addr = input('From: ')
password = input('Password: ')
to_addr = input('To: ')
smtp_server = input('SMTP server: ')
# 邮件对象:
msg = MIMEMultipart('alternative')
msg['From'] = _format_addr('自己 <%s>' % from_addr)
msg['To'] = _format_addr('张子豪 <%s>' % to_addr)
msg['Subject'] = Header('SMTP测试……', 'utf-8').encode()
msg.attach(MIMEText('看到这行说明你该换电脑了!!!', 'plain', 'utf-8'))
# 邮件正文是MIMEText:
msg.attach(MIMEText('<h1>Hello</h1>' +
'<p><img src="cid:0"></p>' + # 引用附件的图片,将其放入正文当中
r.text, 'html', 'utf-8'))
# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open(r'C:\Users\Administrator\Desktop\img\1.jpg', 'rb') as f:
# 设置附件的MIME和文件名,这里是jpg类型:
mime = MIMEBase('image', 'jpg', filename='test.jpg')
# 加上必要的头信息:
mime.add_header('Content-Disposition', 'attachment', filename='test.jpg')
mime.add_header('Content-ID', '<0>') # 给附件起个id值,说白了就是唯一标识
mime.add_header('X-Attachment-Id', '0') # 这行跟网络连接有关,如果不加会报socket.gaierror
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码:
encoders.encode_base64(mime)
# 添加到MIMEMultipart:
msg.attach(mime)
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password) # 与SMTP服务器连接
server.sendmail(from_addr, [to_addr], msg.as_string()) # 发送信息
server.quit()
效果和之前的一样
如果你没有指定MIMEMultipart的
subtype,即没有传参,那么最终的效果是下面这样。
加密SMTP
使用标准的25端口连接SMTP服务器时,使用的是明文传输,发送邮件的整个过程可能会被窃听。要更安全地发送邮件,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件。
某些邮件服务商,例如Gmail,提供的SMTP服务必须要加密传输。我们来看看如何通过Gmail提供的安全SMTP发送邮件。
必须知道,Gmail的SMTP端口是587,因此,修改代码如下:
smtp_server = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代码和前面的一模一样:
server.set_debuglevel(1)
...
只需要在创建SMTP
对象后,立刻调用starttls()
方法,就创建了安全连接。后面的代码和前面的发送邮件代码完全一样。