01. 邮件相关的基础知识
1.1 SMTP/SMTPS协议
- SMTP协议(Port: 25):简单邮件传输协议,是一个基于文本的协议。在其上指定了一个消息的一个或多个接收者(收件人和抄送人),然后消息文本会被传输。
- 可以使用简单的Telnet程序来测试一个SMTP服务器。
- 如今绝大多数邮件服务器都使用该协议。当你给别人发邮件时,你的服务器的某个动态端口(大于1024)就会和邮件服务器的25端口建立一个连接,你发送的邮件会通过这个连接传送到邮件服务器上,保存起来。
- 需要注意的是,现在国内外的大云主机商(例如:阿里云),多数不允许连接外网的25端口。
SMTPS(SMTP-over-SSL)协议:是SMTP协议基于SSL安全协议之上的一种变种协议,它继承了SSL安全协议的非对称加密的高度安全可靠性,可防止邮件泄露。
- SMTPS和SMTP协议一样,也是用来发送邮件的,只是更安全些,防止邮件被黑客截取泄密,还可实现邮件发送者抗抵赖功能,防止发送者发送之后删除已发邮件,拒不承认发送过这样一份邮件。
- 端口465和587便是基于SMTPS协议开放的。
smtplib:用于登录邮件发送平台。(需要获取邮件平台的SMTP协议支持以及授权码)
-
02. Python实现邮件发送
2.1 获取SMTP协议支持以及授权码
2.1.1 QQ邮箱版
访问QQ邮箱页面:https://mail.qq.com/,并完成登录。
- 接着点击右上角的设置。

- 接着点击账户,找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务栏,并点击开启POP3/SMTP协议。

- 根据提示要求完成相应的验证。

- 完成验证后,会自动开启SMTP协议,并获得授权码(打码区域)。(若授权码丢失则重启一下POP3/SMTP协议即可)

- 注:点击协议描述文字后面的(如何使用Foxmail等软件收发邮件?)链接,可以查看到POP3服务器与SMTP服务器的域名。
- POP服务器:pop.qq.com、SMTP服务器:smtp.qq.com。
2.1.2 网易163版
- 访问163邮箱页面:https://mail.163.com/,并完成登录。
- 接着点击右上角的设置 >> POP3/SMTP/IMAP。

- 找到POP3/SMTP/IMAP栏,并点击开启POP3/SMTP协议。

- 根据提示要求完成相应的验证。

- 完成验证后,会自动开启SMTP协议,并获得授权码(打码区域)。(若授权码丢失则重启一下POP3/SMTP协议即可)

- 对于163邮箱而言,在该页面的底部的提示栏中可以直接看到相关的服务器地址。
2.2 发送简单的邮件
- 实现逻辑:
- 首先第一步是构造邮件客户端对象,包括指定邮箱的STMP服务器地址,以及协议的端口号(确定是用SMTP协议还是用SMTPS协议)。
- 然后用客户端对象进行登录,需要指定邮箱账户以及对应的授权码。
- 接着就可以构建邮件了,包括邮件的正文信息、邮件的元信息(邮件主题、发件人、收件人)。
- 邮件构建完成之后,使用邮件客户端对象发送邮件即可。
- 代码实现: ```python import smtplib from email.mime.text import MIMEText
定义常量
SENDER = ‘850340745@qq.com’ # 发件人 RECEIVER = ‘xxy1224adam@163.com’ # 收件人,可指定为列表
RECEIVERS = [‘xxy1224adam@163.com’, ‘xxy1224adam@126.com’, ‘739638877@qq.com’] # 指定多个收件人
授权账户,登录邮件发送服务器
smtp_obj = smtplib.SMTP( host=”smtp.qq.com”, # 邮箱平台的SMTP服务器地址 port=smtplib.SMTP_PORT # SMTP_PORT = 25;SMTP_SSL_PORT = 465 ) smtp_obj.login( user=”850340745@qq.com”, # 邮箱账户 password=”xduxqujbcknbbcfg” # 这里不是输入密码,而是输入账户的授权码 )
构造邮件
设置邮件正文
message = MIMEText( _text=”Hello SmtpLib.”, # 邮件的文本内容 _subtype=’plain’, # 指定文本类型,其中plain为普通文本,也可以指定为html用于发送超文本 _charset=’utf-8’ # 默认使用GBK,建议手动指定为UTF-8 )
设置邮件基本信息
message[‘Subject’] = ‘FirstMailByPython’ # 设置邮件主题 message[‘From’] = SENDER # 发件人 message[‘To’] = RECEIVER # 收件人。
message[‘To’] = “,”.join(RECEIVERS) # 发送给多人时,需要将列表数据转换成字符串,多个邮箱之间用逗号连接。
打包并发送邮件
smtp_obj.sendmail( from_addr=SENDER, to_addrs=RECEIVER,
# to_addrs=RECEIVERS,msg=message.as_string() # 将构建好的邮件内容转成字符串即可发送
)
<a name="y3lbG"></a>### 2.2 发送带有附件的邮件- 带有附件的邮件需要由以下三个模块来构造:- `email.mime.text.MIMEText`:构造邮件中的文本内容。- `email.mime.application.MIMEApplication`:构造邮件中的附件内容。- `email.mime.multipart.MIMEMultipart`:构建总邮件(将文本内容和邮件内容合并到一起)。- 基本实现思路:- 定义发件人和收件人,并授权登录。- 构造邮件(包括邮件主题、发件人、收件人、邮件文本内容、邮件附件内容)。- 发送邮件。- 示例:将路径为`./img/image_1.png`和`./img/image_2.png`的两张图片作为附件发送出去。```pythonfrom email.mime.multipart import MIMEMultipart # 总邮件from email.mime.application import MIMEApplication # 构造附件from email.mime.text import MIMEText # 构造文本import smtplib# 收发件人SENDER = '850340745@qq.com'RECEIVER = 'xxy1224adam@163.com'# 授权登录smtp_obj = smtplib.SMTP(host="smtp.qq.com", port=smtplib.SMTP_PORT)smtp_obj.login(user="850340745@qq.com", password="qukqitrdpsrkbcih")# 构造邮件## 设置邮件基本信息message = MIMEMultipart() # 构造总邮件message['Subject'] = '带附件的邮件' # 设置邮件主题message['From'] = SENDER # 发件人message['To'] = RECEIVER # 收件人## 设置邮件文本内容text = MIMEText(_text="附件是两张图片", _subtype='plain', _charset='utf-8')message.attach(text) # 将文本内容拼接到总邮件中## 设置邮件附件内容### 读取附件数据(发送附件的本质是将附件文件的数据发送过去,因此需要先把数据读出来)with open('./img/image_1.png', 'rb') as file1, open('./img/image_2.png', 'rb') as file2:img_data1 = file1.read()img_data2 = file2.read()### 构造附件1enclosure_1 = MIMEApplication(_data=img_data1) # 构造附件"""add_header()用于构造附件的头部信息,共有三个参数(其中前两个是固定的):参数1:固定为字符串'content-disposition',用于描述附件文件的数据处理方式;参数2:固定为字符串'attachment',表示附件的内容处理方式为拼接;参数3filename:附件的文件名(可与附件的原始文件名不一致)。"""enclosure_1.add_header('content-disposition', 'attachment', filename='附件图片1.png') # 构造头部信息message.attach(enclosure_1) # 将附件1拼接到总邮件中### 构造附件2enclosure_2 = MIMEApplication(_data=img_data2)enclosure_2.add_header('content-disposition', 'attachment', filename='附件图片2.png')message.attach(enclosure_2)# 发送邮件smtp_obj.sendmail(from_addr=SENDER, to_addrs=RECEIVER, msg=message.as_string())
2.3 封装邮件发送功能
- 若要让一个函数可以发送一封邮件,则这个函数压根具有以下参数:
- server:定义邮件发送平台的服务器地址。
- sender:发送者(即用户邮箱地址)。
- password:账户的授权码。
- receivers:接收者(因为可能只有一个接收者,也可能有多个接收者,所以receivers即可以是一个字符串,也可以是一个列表)。
- subject:邮件的主题。
- content:邮件的文本内容(有些邮件可能只有附件,没有文本内容,故存在默认值
''空字符)。 - file_paths:附件的路径。
- 有些邮件可能只有文本内容,没有附件,故存在默认值None。
- 有些邮件可能有多个附件,因此这里传入的应该是一个列表。
- 函数实现: ```python from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.mime.text import MIMEText import smtplib, os
def send_email(server, sender, password, receivers, subject, content=’’, file_paths=None):
# 构建邮件基本信息message = MIMEMultipart() # 邮件可能存在附件,为了保证通用性,因此需要构建总邮件。message['Subject'] = subject # 设置邮件主题message['From'] = sender # 设置发件人# 若receivers是一个字符串,代表收件人只有一个;# 若receivers是个列表,则代表收件人有多个,需要用逗号拼起来。message['To'] = receivers if type(receivers) == str else ",".join(receivers) # 设置收件人# 构建文本内容text = MIMEText(_text=content, _subtype='plain', _charset='utf-8')message.attach(text)# 构建附件内容if file_paths is not None:# 若file_paths不为None,则说明有附件需要发送。for file_path in file_paths:# 跟据路径读取文件数据。with open(file_path, 'rb') as file:file_data = file.read()# 构造附件enclosure = MIMEApplication(_data=file_data)enclosure.add_header('content-disposition', 'attachment', filename=os.path.basename(file_path))# 拼接附件message.attach(enclosure)# 登录邮件并发送smtp_obj = smtplib.SMTP(host=server, port=smtplib.SMTP_PORT)smtp_obj.login(user=sender, password=password)smtp_obj.sendmail(from_addr=sender, to_addrs=receivers, msg=message.as_string())
send_email( server=”smtp.qq.com”, sender=”850340745@qq.com”, password=”qukqitrdpsrkbcih”, receivers=[‘xxy1224adam@163.com’, ‘xxy1224adam@126.com’, ‘739638877@qq.com’], subject=”邮件封装测试”, content=”这封邮件有两张图片”, file_paths=[‘./img/image_1.png’, ‘./img/image_2.png’] )
<a name="bWNPg"></a>### 2.4 定时邮件<a name="YJDJY"></a>#### 2.4.1 定时任务介绍- Python中可以使用schedule这个第三方模块来完成定时任务的编写,因此需要先安装schedule模块。```pythonpip install schedule
schedule.every(10).seconds.do(job) # 每10秒种执行job函数 schedule.every(10).minutes.do(job) # 每10分钟执行job函数 schedule.every().hour.do(job) # 每1小时执行job函数 schedule.every().day.at(“10:30”).do(job) # 每天的10:30执行job函数 schedule.every(5).to(10).minutes.do(job) # 每5~10分种执行一次job函数 schedule.every().monday.do(job) # 每个星期一的00:00执行job函数 schedule.every().wednesday.at(“13:15”).do(job) # 每个星期三的13:15执行job函数 schedule.every().minute.at(“:17”).do(job) # 每分钟的第17秒执行job函数
<a name="cnbko"></a>#### 2.4.2 定时邮件发送- 示例:每天的09:59发送一封邮件,邮件和2.3中的一致即可。```pythonimport schedule# do()的第一参数是定时任务函数的函数名# 后面可以用可变参数将定时任务函数的参数传进去schedule.every().day.at("09:59").do(send_email,server="smtp.qq.com",sender="850340745@qq.com",password="qukqitrdpsrkbcih",receivers=['xxy1224adam@163.com', 'xxy1224adam@126.com', '739638877@qq.com'],subject="邮件封装测试",content="这封邮件有两张图片",file_paths=['./img/image_1.png', './img/image_2.png'])# 因为是定时任务,所以程序要一直开着while True:schedule.run_pending() # 运行定时任务队列
