Skip to content

前言

本篇博客展示了通过Python发邮件,包括通过selenium自动登录163发邮件、通过Django发邮件、通过SMTP发邮件。 但,只求会用,所以仅是示例,不会有太多的说明........

通过selenium自动登录163发邮件

参考我的另一篇博客

通过Django发邮件

参考我的另一篇博客

通过SMTP发邮件

首先要了解几个协议:

  • SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。它定义了邮件客户端和SMTP邮件服务器之间,以及两台SMTP邮件服务器之间的通信规则。
  • POP3(Post Office Protocol),邮局协议,它定义了邮件客户端软件和POP3邮件服务器的通信规则。
  • IMAP(Internet Message Access Protocol),消息访问协议,它是POP3协议的一种扩展。
  • Exchange Server 是微软公司的一套电子邮件服务组件,是个消息与协作系统。 简单而言,Exchange server可以被用来构架应用于企业、学校的邮件系统。
  • CardDAV是一种通讯录同步的开放协议。使用 CardDAV 同步的通讯录可以编辑、修改或者删除,并且你在手机上的这些操作也同样会和服务器同步,并同时同步到你的其他设备上。

下图演示了用户A从QQ邮箱发送邮件到用户B的163邮箱的过程: 1832670384579674112.png

Python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装。

一般的,我们可以在本地搭建支持SMTP的服务,如sendmail,但为了省事,我们可以使用其他的邮件服务商的SMTP服务访问,如QQ、网易等。

这里以QQ邮箱为例,首先要拿到授权码,这里登录你的QQ邮箱,在设置中,选择账号选项,下拉到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务项,获取授权码。 1832670386022514688.png 然后手机发送短信验证。 1832670386299338752.png

然后点击我已发送,就出现了授权码页面。 1832670386861375488.png

有了授权码,开干!

发送普通文本邮件

代码示例:

python
import smtplib
from email.mime.text import MIMEText
from email.header import Header

# 第三方 SMTP 服务
mail_host = "smtp.qq.com"  # 设置服务器
mail_user = "1206180814@qq.com"  # 用户名
mail_pass = "dfpcglacrjbybafa"  # 获取授权码
sender = '1206180814@qq.com'  # 发件人账号
receivers = ['1206180814@qq.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
send_content = 'Python 邮件发送测试...'
message = MIMEText(send_content, 'plain', 'utf-8')  # 第一个参数为邮件内容,第二个设置文本格式,第三个设置编码
message['From'] = Header("我是发件人", 'utf-8')  # 发件人
message['To'] = Header("我是收件人", 'utf-8')   # 收件人

subject = '邮件大标题'
message['Subject'] = Header(subject, 'utf-8')
try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")
except smtplib.SMTPException:
    print("Error: 无法发送邮件")

运行结果如下:

1832670387037536256.png

发送HTML格式邮件

授权码都不变,只需将MIMEText的第二个参数的文本类型改一下即可:

python
import smtplib
from email.mime.text import MIMEText
from email.header import Header

# 第三方 SMTP 服务
mail_host = "smtp.qq.com"  # 设置服务器
mail_user = "1206180814@qq.com"  # 用户名
mail_pass = "dfpcglacrjbybafa"  # 口令

sender = '1206180814@qq.com'
receivers = ['1206180814@qq.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
send_content = """
<h1>天不生我李淳罡</h1>
<h1>剑道万古如长夜</h1>
<p>小二上酒</p>
<img src="https://ss0.baidu.com/73t1bjeh1BF3odCf/it/u=858168512,2130327819&fm=85&s=2E4020DF1CD035FBDC9D940A0300F0F3">
<div>阅读请&nbsp;<a href="https://www.37zw.net/0/761/">点我,点我</a></div>
"""
message = MIMEText(send_content, 'html', 'utf-8')  # 第一个参数为邮件内容
message['From'] = Header("我是发件人", 'utf-8')  # 发件人
message['To'] = Header("我是收件人", 'utf-8')  # 收件人

subject = '雪中悍刀行'
message['Subject'] = Header(subject, 'utf-8')

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")

except smtplib.SMTPException:
    print("Error: 无法发送邮件")

运行结果展示:

1832670387247251456.png

发送HTML中带本地图片的邮件

上一个示例中的图片,是一个远程连接,那么我们要发送本地的图片,就要采用下面的方式了:

python
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.header import Header

# 第三方 SMTP 服务
mail_host = "smtp.qq.com"  # 设置服务器
mail_user = "1206180814@qq.com"  # 用户名
mail_pass = "dfpcglacrjbybafa"  # 口令

sender = '1206180814@qq.com'
receivers = ['1206180814@qq.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱

message = MIMEMultipart('related')
message['From'] = Header("我是发件人", 'utf-8')  # 发件人
message['To'] = Header("我是收件人", 'utf-8')  # 收件人

subject = '雪中悍刀行--本地图片版'
message['Subject'] = Header(subject, 'utf-8')

msg = MIMEMultipart('alternative')
message.attach(msg)


send_content = """
<h1>天不生我李淳罡</h1>
<h1>剑道万古如长夜</h1>
<p>小二上酒</p>
<img src="cid:image">
<div>阅读请&nbsp;<a href="https://www.37zw.net/0/761/">点我,点我</a></div>
"""
msg.attach(MIMEText(send_content, 'html', 'utf-8'))  # 第一个参数为邮件内容

# 读取当前目录下的图片
f = open('img.jpg', 'rb')
img_msg = MIMEImage(f.read())
f.close()

# 定义图片在HTML文本中的位置
img_msg.add_header('Content-ID', '<image>')   # 根据id定位
message.attach(img_msg)


try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")

except smtplib.SMTPException:
    print("Error: 无法发送邮件")

运行结果:

1832670387507298304.png

发送带各式类型附件的邮件

发送带附件的邮件,首先要创建MIMEMultipart实例,然后在构建附件,如果有多个附件的话,可依次构建,最后利用smtplib.smtp发送:

python
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

# 第三方 SMTP 服务
mail_host = "smtp.qq.com"  # 设置服务器
mail_user = "1206180814@qq.com"  # 用户名
mail_pass = "dfpcglacrjbybafa"  # 口令

sender = '1206180814@qq.com'
receivers = ['1206180814@qq.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 创建一个带附件的实例
message = MIMEMultipart()
message['From'] = Header("我是发件人", 'utf-8')  # 发件人
message['To'] = Header("我是收件人", 'utf-8')   # 收件人

subject = 'Python发送带附件的邮件示例'
message['Subject'] = Header(subject, 'utf-8')

# 邮件正文内容
send_content = 'hi man,你收到附件了吗?'
content_obj = MIMEText(send_content, 'plain', 'utf-8')  # 第一个参数为邮件内容
message.attach(content_obj)

# 构造附件1,发送当前目录下的 t1.txt 文件
att1 = MIMEText(open('t1.txt', 'rb').read(), 'base64', 'utf-8')
att1["Content-Type"] = 'application/octet-stream'
# 这里的filename可以任意写,写什么名字,邮件附件中显示什么名字
att1["Content-Disposition"] = 'attachment; filename="t1.txt"'
message.attach(att1)

# 构造附件2,发送当前目录下的 t2.py 文件
att2 = MIMEText(open('t2.py', 'rb').read(), 'base64', 'utf-8')
att2["Content-Type"] = 'application/octet-stream'
att2["Content-Disposition"] = 'attachment; filename="t2.py"'
message.attach(att2)
try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")

except smtplib.SMTPException:
    print("Error: 无法发送邮件")

运行结果如下:

1832670387914145792.png

如果要发送其他类型的,如果PDF、doc、xls、MP3格式的,我们都可以通过MIMEApplication来完成,MIMEApplication默认子类型是application/octet-stream,而application/octet-stream表明这是个二进制文件,但愿接收方知道怎么处理!!!,然后客户端收到这个声明后会根据文件扩展名来猜测。

python
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header

# 第三方 SMTP 服务
mail_host = "smtp.qq.com"  # 设置服务器
mail_user = "1206180814@qq.com"  # 用户名
mail_pass = "dfpcglacrjbybafa"  # 口令

sender = '1206180814@qq.com'
receivers = ['1206180814@qq.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 创建一个带附件的实例
message = MIMEMultipart()
message['From'] = Header("我是发件人", 'utf-8')  # 发件人
message['To'] = Header("我是收件人", 'utf-8')   # 收件人

subject = 'Python发送带附件的邮件示例'
message['Subject'] = Header(subject, 'utf-8')

# 邮件正文内容
send_content = 'hi man,你收到附件了吗?'
content_obj = MIMEText(send_content, 'plain', 'utf-8')  # 第一个参数为邮件内容
message.attach(content_obj)

# 构造附件1,发送当前目录下的 t1.txt 文件
part1 = MIMEApplication(open('t1.txt', 'rb').read())
part1.add_header('Content-Disposition', 'attachment', filename='t1.txt')
message.attach(part1)


# 构造附件2,发送当前目录下的 bg.mp3 文件
part2 = MIMEApplication(open('bg.mp3', 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename='bg.mp3')
message.attach(part2)


# 构造附件3,发送当前目录下的 t3.xls 文件
part3 = MIMEApplication(open('t3.xls', 'rb').read())
part3.add_header('Content-Disposition', 'attachment', filename='t3.xls')
message.attach(part3)

# 构造附件4,发送当前目录下的 t4.doc 文件
part4 = MIMEApplication(open('t4.doc', 'rb').read())
part4.add_header('Content-Disposition', 'attachment', filename='t4.doc')
message.attach(part4)


# 构造附件5,发送当前目录下的 t5.pdf 文件
part5 = MIMEApplication(open('t5.pdf', 'rb').read())
part5.add_header('Content-Disposition', 'attachment', filename='t5.pdf')
message.attach(part5)

# 构造附件6,发送当前目录下的 img.jpg 文件
part6 = MIMEApplication(open('img.jpg', 'rb').read())
part6.add_header('Content-Disposition', 'attachment', filename='img.jpg')
message.attach(part6)

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)  # 25 为 SMTP 端口号
    smtpObj.login(mail_user, mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")

except smtplib.SMTPException:
    print("Error: 无法发送邮件")

结果如下:

1832670388203552768.png

通过smtplib.SMTP_SSL发送邮件

python
import smtplib
from email.utils import formataddr
from email.mime.text import MIMEText

# 通过smtplib.SMTP_SSL发送邮件
mail_host = "smtp.163.com"  # 网易邮箱服务器
mail_user = "tingyuweilou@163.com"  # 账号
mail_token = "WLWFMHNAYBCARUTP"  # 获取的授权码
sender = "1206180814@qq.com"  # 发件人邮件
# 收件人可以写一个
to = "1206180814@qq.com"
# 如果是多个收件人,就以列表的形式发送就行了
to_list = ["1206180814@qq.com", "tingyuweilou@163.com"]
msg = MIMEText("邮件正文内容", "html", "utf-8")  # # 第一个参数为邮件内容,第二个设置文本格式,第三个设置编码
msg["From"] = formataddr(["张开", sender])
msg['To'] = to
msg["Subject"] = "邮件大标题"

server = smtplib.SMTP_SSL(mail_host)
server.login(mail_user, mail_token)
server.sendmail(mail_user, to_list, msg.as_string())
server.quit()

批量下载指定时间范围内的邮件附件

参考:https://blog.51cto.com/u_13416/8389094

下面代码以QQ邮箱为例。

python
import email
import os
import poplib
import time
import os
from email.header import decode_header
from email.parser import Parser

BASEDIR = os.path.dirname(os.path.abspath(__file__))

def decode_str(s):
    """
    字符编码转换
    """
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value


host = "smtp.qq.com"  # 服务器域名,QQ邮箱的话,填写 pop.qq.com 也行
server = poplib.POP3(host)  # 建立链接
username = "xxxxx@qq.com"  # QQ邮箱账号
password = "xxxxx"  # 密码IMAP/SMTP的授权码,非登录密码,需要到qq邮箱开启smtp和pop3服务,然后获取那个授权码

# 时间范围选择
start_date = '20240430'   # 设置开始日期
end_date = '20240530'     # 设置结束日期
# 设置储存路径
mail_att_dir = os.path.join(BASEDIR, 'dat')  # 邮件附件存储路径,可以任意指定
# 登录
server.user(username)
server.pass_(password)
print('认证通过!')
resp, mails, octets = server.list()  # 获取所有邮件编号,mails的格式为['mesg_num octets', ...]
print('获取所有邮件编号成功,共%d封邮件!' % len(mails))
# 倒序遍历邮件
index = len(mails)
for i in range(index, 0, -1):
    # lines存储了邮件的原始文本的每一行
    resp, lines, octets = server.retr(i)
    # 邮件的原始文本:
    msg_content = b'\r\n'.join(lines).decode("utf-8", 'ignore')
    # 解析邮件:
    msg = Parser().parsestr(msg_content)
    # 获取邮件日期
    date = time.strptime(msg.get('Date')[:24], '%a, %d %b %Y %H:%M:%S')
    date = time.strftime('%Y%m%d', date)
    # 只下载发送日期在指定日期之间的邮件附件
    if date < start_date:
        break
    if date > end_date:
        continue
    # 下载附件
    attachment_files = []
    for part in msg.walk():
        file_name = part.get_filename()  # 获取附件名称类型
        contType = part.get_content_type()
        if file_name:
            h = email.header.Header(file_name)
            dh = email.header.decode_header(h)  # 对附件名称进行解码
            filename = dh[0][0]
            if dh[0][1]:
                filename = decode_str(str(filename, dh[0][1]))  # 将附件名称可读化
            data = part.get_payload(decode=True)  # 下载附件
            # 创建附件存储文件夹
            if not os.path.isdir(mail_att_dir):
                os.mkdir(mail_att_dir)
            # 在指定目录下创建文件,注意二进制文件需要用wb模式打开
            att_file = open(mail_att_dir + os.sep + filename, 'wb')
            attachment_files.append(filename)
            att_file.write(data)  # 保存附件
            att_file.close()
server.quit()
print('下载完成,去文件夹看看吧!')
# 代码截止2024/4/30运行正常

常见报错

smtplib.SMTPHeloError: (500,b'Error: bad syntax')

Python3.10 + Windows11

Python脚本通过smtplib模块发送邮件,报错了如下错误: 1832670388526514176.png

这个报错是Windows系统独有的报错,而且并不常见,原因如下(我是谷歌得到来的,并不肯定是真正的原因):

1832670389130493952.png

解决方案,就是调整dns后缀的值就好了,在控制面板->网络和internal,找到网络中心。 1832670389457649664.png 然后就如下操作吧: 1832670389768028160.png 填写完HOST后缀之后,一顿确定就行了。

欢迎斧正,that's all see also:Python SMTP发送邮件 | python - 发送带各种类型附件的邮件