python邮件发送

python中邮件发送可以使用smtplib模块,也可以使用第三方的yagmail模块。

邮件收发简介

假如从xx@126.com发送一份邮件至xx@sina.com。首先使用Foxmail等软件写好邮件发送,这些软件称为MUA(Mail User Agent,邮件用户代理)。
邮件从MUA发出去之后,先发到MTA(Mail Transfer Agent,邮件传输代理),也就是邮件服务提供商。我们的邮件是从126.com发送的,所以先被投递到网易提供的MTA,再由网易的MTA发到对方服务商,也就是新浪的MTA。
邮件到达新浪的MTA后,由于对方使用的是新浪的邮箱,因此新浪的MTA会将邮件投递至最终的目的地MDA(Mail Delivery Agent,邮件投递代理)。
邮件到达MDA之后就会存放在某个服务器,我们将这个长期保存邮件的地方称为电子邮箱。
编写程序发送邮件本质就是编写MUA把邮件发送到MTA;收取邮件就是编写MUA从MDA收邮件。

发邮件时,MUA和MTA使用额协议就是SMTP(Simple Mail Transfer Protocol),后面的MTA到另一个MTA也是使用SMTP协议。
收邮件时,MUA和MDA使用的协议有两种,POP(Post Office Protocol),目前版本是3,称为POP3IMAP(Internet Message Access Protocol)目前版本是4,优点是不但能收取邮件,还可以直接操作MDA上存储的邮件,比如从收件箱移动到垃圾箱等。
另外使用python发送邮件时需要注意:

  1. 大多数邮件服务商需要手动打开SMTP发信和POP收信功能。否则只能网页登录。
  2. 邮件客户端大多数要求使用客户端授权密码,而不是邮箱密码。需要网页登录设置。

smtplib

python对SMTP支持的有smtplibemail2个模块,其中email负责构造邮件,smtplib负责发送邮件。

email构造邮件

邮件构造主要使用email.mime

  • 纯文本邮件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from email import encoders
    from email.header import Header
    from email.mime.text import MIMEText
    from email.utils import parseaddr, formataddr

    def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

    msg_from='XXXXX@163.com'
    passwd='XXXXX'
    msg_to='XXXXX@qq.com'
    receivers = ['XXXXX@qq.com']
    subject="python邮件测试"
    content="hello, send by Python..."

    msg = MIMEText(content,'plain','utf-8')
    msg['Subject'] = Header(subject,'utf-8').encode()
    msg['From'] = _format_addr('xiaohh <%s>' %msg_from) #此处xiaohh表示发件人的昵称
    msg['To'] = msg_to

    msg = MIMEText(content, 'plain', 'utf-8')

_format_addr()用于格式化邮件地址。如果包含中文,需要通过Header对象进行编码
构造MIMEText对象时,第一个参数是邮件正文,第二个参数是MIME的subtype,传入plain表示文本,最终的MIME就是text/plain,最后使用utf-8编码。
msg['T0']接受的是字符串而不是列表,若有多个地址可使用,分隔。

  • HTML邮件
    在构造MIMEText对象时,把html字符串传进去,再把第二个参数由plain变为html

    1
    2
    3
    4
    content = ''<html><body><h1>Hello</h1>' +
    '<p>send by <a href="http://www.python.org">Python</a>...</p>' +
    '</body></html>''
    msg = MIMEText(content, 'plain', 'utf-8')
  • 发送附件
    带附件的邮件可以看做包含若干部分的邮件:文本和各个附件本身,可以构造一个MIMEMultipart对象代表邮件本身,然后往里面添加MIMEText作为邮件正文,再继续往里面加上表示附件的MIMEBase对象即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    msg = MIMEMultipart()
    msg.attach(MIMEText('send with file...', 'plain', 'utf-8')) #邮件正文
    with open('/Users/michael/Downloads/test.png', 'rb') as f:
    mime = MIMEBase('image', 'png', filename='test.png') # 设置附件的MIME和文件名,这里是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()) # 把附件的内容读进来
    encoders.encode_base64(mime) # 用Base64编码
    msg.attach(mime) # 添加到MIMEMultipart
  • 发送图片
    要想把图片嵌入到邮件正文中,直接在HTML邮件中链接图片地址是不可行的,因为邮件服务商无法确定链接是否指向恶意网站。
    要把图片嵌入到正文中,需要按照发送附件的方式,先把图片作为附件添加进去,然后在HTML中通过引用src="cid:0"就可以把附件作为图片嵌入。如果有多个图片,给它们依次编号,然后引用不同的cid:x即可。

    1
    2
    3
    msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
    '<p><img src="cid:0"></p>' +
    '</body></html>', 'html', 'utf-8'))

关于email.mime更多的说明可以点击email.mime: Creating email and MIME objects from scratch查看官方介绍。
关于email邮件构造和解析更多的说明可以点击email — An email and MIME handling package查看官方介绍。

smtplib发送邮件

smtplib发送邮件,实例化SMTP(),指定smtp_server地址和端口号;
调用logine()方法登录SMTP服务器;
调用sendmail()方法发邮件。

1
2
3
4
5
6
import smtplib

server = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

由于邮件正文是str,所以msg.as_string()是将MIMEText对象变成str

  • 加密SMTP
    使用25端口连接SMTP服务器时使用的是明文传输,要更安全的发送邮件,可以加密SMTP会话。

starttls()

1
2
3
4
5
server = smtplib.SMTP('smtp.gmail.com', 587) # gmail加密传输端口是587
server.starttls() #创建安全连接
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

调用starttls()方式创建安全连接,其余代码和前面相同。
smtp_ssl()
在连接建立的时候就需要SSL并且不适合使用starttls()的情况下可以选择使用smtp_ssl()

1
2
3
4
server = smtplib.SMTP_SSL(smtp_server, 465) #使用smtp_ssl()方法
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

备注:starttls()使用TLS,默认端口587;smtp_ssl()使用SSL,默认端口为465。此处需要根据邮件服务商提供的加密方式选择。
关于smtplib更多的说明可以点击smtplib — SMTP protocol client查看官方介绍。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from email import encoders
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)
return formataddr((Header(name, 'utf-8').encode(), addr))

msg_from='XXXXX@163.com'
passwd='XXXXX'
msg_to='XXXXX@qq.com'
receivers = ['XXXXX@qq.com']
subject="python邮件测试"
content="hello, send by Python..."

msg = MIMEText(content,'plain','utf-8')
msg['Subject'] = Header(subject,'utf-8').encode()
msg['From'] = _format_addr('xiaohh <%s>' %msg_from) #此处xiaohh表示发件人的昵称
msg['To'] = msg_to

msg = MIMEText(content, 'plain', 'utf-8')

server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

yagmail

yagmail是一个第三方模块,点击yagmail可以在github查看该项目使用说明及源码。
使用yagmail发送邮件简单方便,免去了构建邮件内容的繁琐步骤。

安装

python2和python3的安装如下

1
2
pip install yagmail
pip3 install yagmail

使用

实例化SMTP()类,然后调用send()方法即可。
更多使用可以参考github项目主页说明。

实例

1
2
3
4
5
6
7
8
9
10
11
import yagmail

args = {
"user": "xxxx@163.com",
"password": "xxxx",
"host": "smtp.163.com",
"port": "465"
}
emailList = ['xxx@qq.com']
yag = yagmail.SMTP(**args)
yag.send(to=emailList, subject="python邮件", contents="Thanks for your python email!", attachments="__init__.py")

to:收件人,多个收件人可以通过列表方式传递。
subject:邮件主题。
contents:邮件正文。
attachments:附件,可以使用绝对路径。此处表示该文件夹和代码在同一目录。
另外,此处的contents可以传递列表,它可以自动识别文件格式。例如:
上面发送修改为如下格式

1
yag.send(to=emailList, subject="python邮件", contents=["Thanks for your python email!","__init__.py"])

最后收到邮件效果和上面的代码一致。可自动将__init__.py识别为附件并添加。

Recommended Posts