在 Python 中发送电子邮件
我们将探讨在 Python 中发送邮件的基本知识
Python 提供了一些非常实用的模块,可用于构建电子邮件。它们是 email 模块和 smtplib 模块。我们不会逐一讲解这两个模块的各种方法,而是花些时间学习如何实际使用它们。具体来说,我们将涵盖以下内容:
- 邮件发送的基础知识
- 如何一次性发送到多个收件人
- 如何使用 TO、CC 和 BCC 字段发送邮件
- 如何使用 email 模块添加附件和邮件正文
让我们开始吧!
邮件基础 —— 使用 smtplib 发送邮件
smtplib 模块非常易于使用。我们来写一个简单的示例,展示如何发送一封邮件。将以下代码保存到你的硬盘上的一个文件中:
1import smtplib 2 3HOST = "mySMTP.server.com" 4SUBJECT = "Test email from Python" 5TO = "mike@someAddress.org" 6FROM = "python@mydomain.com" 7text = "Python 3.4 rules them all!" 8 9BODY = "\r\n".join(( 10 "From: %s" % FROM, 11 "To: %s" % TO, 12 "Subject: %s" % SUBJECT, 13 "", 14 text 15)) 16 17server = smtplib.SMTP(HOST) 18server.sendmail(FROM, [TO], BODY) 19server.quit()
我们导入了 smtplib 模块。这段代码的三分之二用于设置邮件内容。大多数变量都很直观,因此我们只关注其中一个特殊变量:BODY。这里我们使用字符串的 join 方法将所有之前的变量组合成一个字符串,每行以回车符("\r")加换行符("\n")结尾。如果你打印出 BODY,结果会是这样:
1'From: python@mydomain.com\r\nTo: mike@someAddress.org\r\nSubject: Test email from Python\r\n\r\nPython 3.4 rules them all!'
接下来,我们建立与主机的服务器连接,然后调用 smtplib 模块的 sendmail 方法发送邮件,最后断开连接。你可能会注意到,这段代码中没有用户名或密码。如果您的邮件服务器需要身份验证,则需要添加以下代码:
1server.login(username, password)
这行代码应紧接在创建服务器对象之后添加。通常,你会希望将这部分代码封装进函数,并传入相关参数。你甚至可以将部分信息存入配置文件中。下面我们把代码封装成一个函数。
1import smtplib 2 3def send_email(host, subject, to_addr, from_addr, body_text): 4 """ 5 发送邮件 6 """ 7 BODY = "\r\n".join(( 8 "From: %s" % from_addr, 9 "To: %s" % to_addr, 10 "Subject: %s" % subject, 11 "", 12 body_text 13 )) 14 server = smtplib.SMTP(host) 15 server.sendmail(from_addr, [to_addr], BODY) 16 server.quit() 17 18if __name__ == "__main__": 19 host = "mySMTP.server.com" 20 subject = "Test email from Python" 21 to_addr = "mike@someAddress.org" 22 from_addr = "python@mydomain.com" 23 body_text = "Python rules them all!" 24 send_email(host, subject, to_addr, from_addr, body_text)
现在你可以看到,仅从函数本身来看,实际代码有多么简洁——只有13行!如果我们不把 BODY 中的每一项单独分行写,还能更短一些,但可读性会下降。接下来,我们将把服务器信息和 from 地址放入配置文件中。为什么这么做?因为在我的工作中,我们可能使用不同的邮件服务器发送邮件,或者当邮件服务器升级导致名称变更时,只需修改配置文件,而无需改动代码。同样的情况也适用于 from 地址,比如公司被收购合并后。
让我们看一下配置文件(保存为 email.ini):
1[smtp] 2server = some.server.com 3from_addr = python@mydomain.com
这是一个非常简单的配置文件。其中包含一个名为 smtp 的节,里面有两个字段:server 和 from_addr。我们将使用 ConfigParser 来读取该文件。以下是更新后的代码(保存为 smtp_config.py):
1import os 2import smtplib 3import sys 4 5from configparser import ConfigParser 6 7def send_email(subject, to_addr, body_text): 8 """ 9 发送邮件 10 """ 11 base_path = os.path.dirname(os.path.abspath(__file__)) 12 config_path = os.path.join(base_path, "email.ini") 13 14 if os.path.exists(config_path): 15 cfg = ConfigParser() 16 cfg.read(config_path) 17 else: 18 print("配置文件未找到!正在退出!") 19 sys.exit(1) 20 21 host = cfg.get("smtp", "server") 22 from_addr = cfg.get("smtp", "from_addr") 23 24 BODY = "\r\n".join(( 25 "From: %s" % from_addr, 26 "To: %s" % to_addr, 27 "Subject: %s" % subject, 28 "", 29 body_text 30 )) 31 server = smtplib.SMTP(host) 32 server.sendmail(from_addr, [to_addr], BODY) 33 server.quit() 34 35if __name__ == "__main__": 36 subject = "Test email from Python" 37 to_addr = "mike@someAddress.org" 38 body_text = "Python rules them all!" 39 send_email(subject, to_addr, body_text)
我们在这段代码中加入了一点检查逻辑。首先获取脚本所在路径,即 base_path。接着将该路径与文件名拼接,得到配置文件的完整路径。然后检查该文件是否存在。如果存在,就创建一个 ConfigParser;否则输出提示信息并退出脚本。为了更加安全,我们还应该在 ConfigParser.read() 调用周围加上异常处理,因为文件可能存在但损坏,或者没有权限打开,这些都可能引发异常。这可以作为一个小项目由你自己尝试实现。不过假设一切顺利,ConfigParser 对象成功创建,那么我们就可以通过标准的 ConfigParser 语法提取出 host 和 from_addr 信息。
现在,我们已经准备好学习如何同时发送多封邮件了!