使用内置的 smtplib 模块
需要提供的信息
import smtplib
smtpObj = smtplib.SMTP(theSMTPServerDomainName, port)
SMTP 对象包含了连接信息,以及发送邮件的方法
一般来说,SMTP 支持命令加密标准 TLS, 这个标准使用的端口几乎总是 587
若 smtplib.SMTP()
方法调用不成功,则表示 SMTP 服务器不支持 TLS 端口 587,此时可以使用 SSL
smtpObj = smtplib.SMTP_SSL(theSMTPServerDomainName, port)
而这个端口,一般为 465
若没有连网,则将会抛出异常,gaetaddrinfofailed 之类的异常
建立了 SMTP 对象之后,需要确保与服务器的连接正常,必须要调用此方法,否则之后的方法调用将会出错
ehloResult = smtpObj.ehlo()
返回的 ehloResult 是一个元组,若第一项为 250
, 则表示问候成功
250 在 SMTP 中表示成功
这一步使得连接处于加密状态中
tlsResult = smtpObj.starttls()
返回的 tlsResult 是一个元组,若第一项为 220
, 则表示服务器已经准备好了
这一步是使用 TLS 标准加密需要做,使用 SSL(465 端口) 的连接不需要进行这一步,因为加密早已做好了
loginResult = smtpObj.login(yourEmailAddress, password)
返回的 loginResult 是一个元组,若第一项为 235
, 则表示认证成功,若密码错误,则会抛出异常 smtplib.SMTPAuthenticationError
账号密码不应该写在程序中,而应该使用
input()
来获取用户的输入
发送邮件需要 3 个参数:
Subject: xxxx\n
开头,作为主题行\n
后为正文内容sendResult = smtpObj.sendmail(fromWhom, toWhom, content)
返回的结果为一个 dict, 包含的是传送失败的每个收件人
所以,空的 dict 意味着发送成功了
⚠️ Gmail 会需要程序专用密码,设置 Gmail 的应用程序专用密码
quitResult = smtpObj.quit()
返回的 quitResult 是一个原则,若第一项是 221
, 则表示会话结束了
收取邮件,Python 有内置的 imaplib 模块,但第三方 imapclient 模块更易用
由于 imapclient 下载后的电子邮件格式比较复杂,要将格式转换成简单的字符串,使用第三方 pyzmail 模块
需要提供的信息:
import imapclient
imapObj = imapclient.IMAPClient(theIMAPServerDomainName, ssl=True)
由于大多数邮件提供商要求 SSL 加密
imapObj.login(emailAddress, password)
# 将返回一个服务器的响应
搜索电子邮件需要分为 2 步
获取文件夹列表
listResult = imapObj = list_folders()
返回的 listResult 是一个包含多个 tuple 的列表,tuple 的值根据不同的邮件服务提供商而定
选择一个文件夹
selectResult = imapObj.select_folder(folerName, readonly=True)
返回的 selectResult 是一个 dict, 包含了 SELECT 的响应,基本上是可以忽略这个返回值
readOnly 是为了防止之后的操作误对邮件进行的修改,删除操作。同时,设置 readOnly = True
后,也不会影响邮件的已读状态
如果需要更改 readOnly 的值,则需要重新选择文件夹,即在此调用
select_folder
方法
搜索需要使用到搜索键,关于搜索键的种类,看 「Python 编程快速上手——让繁琐工作自动化」的 P310
searchResult = imapObj.search([searchKey1, searchKey2, ...])
返回的 searchResult 为邮件的唯一整数 ID 的列表
获取邮件内容,可以根据邮件的唯一整数 ID, 调用 fetch()
方法获取邮件内容
当搜索匹配大量的邮件,Python 可能会抛出异常
imaplib.error: got more than XXXXX bytes
这是为了防止 Python 消耗过多的内存,此时应该重新连接 IMAP 服务器
可以对默认大小进行更改
import imaplib # 注意是 imaplib
imaplib._MAXLINE = xxxxx
rawMessages = imapObj.fetch(UIDs, ['BODY[]'])
参数:
返回的 rawMessages 是一个字典,以 UID 为键,而每一个键对应的值也是一个字典,包含了 BODY[]
和 SEQ
两个键
BODY[]
邮件的实际正文SEQ
序列号,作用与 UID 类似由于
BODY[]
对应的值内容是 RFC822 格式,专为 IMAP 服务器设计的,不易理解。因此将使用 pyzmail 模块来进行解析
使用 pyzmail 模块,可以轻松访问邮件的主题,正文,收件人,发件人等字段
import pyzmail
message = pyzmail.PyzMessage.factory(rawMessages[mailUID]['BODY[]'])
调用 pyzmail.PyzMessage.factory 方法,传入原始邮件的 BODY[]
部分,返回的结果为 PyzMessage 对象
# 获取邮件主题
message.get_subject()
# 获取发件人
message.get_addresses('from')
# 获取收件人
message.get_addresses('to')
# 获取抄送人
message.get_addresses('cc')
# 获取密件抄送人
message.get_addresses('bcc')
get_addresses
方法返回的是包含 tuple 的列表,其中 tuple 的结果为 (邮箱名, 邮箱地址)
电子邮件的内容可以是
识别电子邮件内容
html_part
置为 None
text_part
置为 None
html_part
与 text_part
都不为 None
获取电子邮件内容
获取纯文本部分
if (message.text_part):
result = message.text_part.get_payload()
获取 HTML 部分
if (message.html_part):
result = message.html_part.get_payload()
由于 get_payload()
方法返回的是 bytes 类型数据,需要调用 decode(编码)
来进行解码
message.html_part.get_payload(message.html_part.charset)
邮件内容的字符编码,保存在了 message.text_part.charset 或 message.html_part.charset 中
在删除邮件前,选择邮箱时,需要将 readOnly
参数设置为 False
deleteResult = imapObj.delete_messages(UIDs)
传入需要删除邮件的 UID 列表
此方法将为邮件添加上 \Delete
标记
返回的 deleteResult 为一个 dict, 每个键值对是一个邮件 ID 及其消息标记
deleteResult = imapObj.expunge()
永久删除带 \Delete
标记的邮件
返回的 deleteResult 是一条消息
imapObj.logout()
若程序运行的时间过长,IMAP 服务器会自动断开断开,此时调用 imapObj 的方法将会抛出异常。此时需要重现调用
imapclient.IMAPClient()
方法