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`的两张图片作为附件发送出去。
```python
from 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()
### 构造附件1
enclosure_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拼接到总邮件中
### 构造附件2
enclosure_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模块。
```python
pip 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中的一致即可。
```python
import 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() # 运行定时任务队列