1 文章背景在 Web 系统或桌面工具中经常会遇到发送邮件的需求例如发送注册验证码、密码找回验证码、系统通知、任务提醒等。Python 标准库中提供了smtplib和email相关模块可以比较方便地完成邮件发送。本文主要整理如何开启邮箱 SMTP 服务并使用 Python 发送 HTML 格式邮件同时封装一个验证码邮件模板。2 SMTP 基本认识SMTP 是 Simple Mail Transfer Protocol 的缩写中文通常称为简单邮件传输协议。它主要用于发送邮件。使用 Python 发送邮件时通常需要准备以下信息① SMTP 服务器地址。② SMTP 端口。③ 发件邮箱账号。④ 邮箱授权码。⑤ 收件人邮箱地址。⑥ 邮件标题和邮件正文。需要注意很多邮箱现在不建议直接使用邮箱登录密码发送邮件而是需要在邮箱设置中开启 SMTP 服务并生成专门的授权码。3 开启邮箱 SMTP 服务不同邮箱开启 SMTP 服务的位置不同但整体流程类似。以常见邮箱为例一般需要① 登录邮箱网页版。② 进入设置页面。③ 找到POP3/SMTP/IMAP相关设置。④ 开启 SMTP 服务。⑤ 根据提示生成授权码。⑥ 在 Python 程序中使用授权码登录 SMTP 服务。这里要特别注意授权码应该当作密码一样保管不能直接写在代码中。4 使用到的 Python 模块本文主要使用以下模块importosimportsecretsimportsmtplibimportstringfromemail.mime.textimportMIMETextfromemail.headerimportHeader其中①smtplib用于连接 SMTP 服务器并发送邮件。②MIMEText用于构造文本或 HTML 邮件内容。③Header用于处理邮件标题编码。④secrets用于生成验证码。⑤string用于获取数字字符集合。⑥os用于从环境变量读取邮箱配置。5 邮箱配置方式为了避免将邮箱账号和授权码写死在代码中建议使用环境变量保存配置。例如可以设置以下环境变量EMAIL_HOSTsmtp.example.com EMAIL_PORT465 EMAIL_USERyour_emailexample.com EMAIL_PASSWORDyour_email_auth_code EMAIL_SENDERyour_emailexample.com其中①EMAIL_HOST表示 SMTP 服务器地址。②EMAIL_PORT表示 SMTP 服务端口。③EMAIL_USER表示登录 SMTP 服务的用户名。④EMAIL_PASSWORD表示邮箱授权码。⑤EMAIL_SENDER表示发件人邮箱地址。如果使用网易邮箱SMTP 地址通常类似smtp.163.com具体地址和端口需要以邮箱服务商的说明为准。6 封装邮件发送类下面封装一个简单的EmailClient类用于发送 HTML 邮件和生成验证码。importsecretsimportsmtplibimportstringfromemail.headerimportHeaderfromemail.mime.textimportMIMETextclassEmailClient:def__init__(self,email_host:str,email_port:int,email_user:str,email_password:str,email_sender:str):self.email_hostemail_host self.email_portemail_port self.email_useremail_user self.email_passwordemail_password self.email_senderemail_senderdefsend_email(self,email_send_to:str,email_title:str,email_content:str)-bool:messageMIMEText(email_content,html,utf-8)message[Subject]Header(email_title,utf-8)message[From]self.email_sender message[To]email_send_totry:withsmtplib.SMTP_SSL(self.email_host,self.email_port)assmtp:smtp.login(self.email_user,self.email_password)smtp.sendmail(self.email_sender,[email_send_to],message.as_string())returnTrueexceptsmtplib.SMTPExceptionaserror:print(f邮件发送失败{error})returnFalsestaticmethoddefcreate_verify_code(length:int6)-str:digitsstring.digitsreturn.join(secrets.choice(digits)for_inrange(length))7 代码说明7.1 初始化参数def__init__(self,email_host:str,email_port:int,email_user:str,email_password:str,email_sender:str):初始化时需要传入 SMTP 服务相关配置。参数含义如下①email_hostSMTP 服务器地址。②email_portSMTP 服务器端口。③email_user邮箱登录账号。④email_password邮箱授权码。⑤email_sender发件人邮箱地址。7.2 构造邮件内容messageMIMEText(email_content,html,utf-8)这里使用MIMEText构造邮件正文。参数含义如下①email_content表示邮件正文。②html表示正文内容是 HTML 格式。③utf-8表示使用 UTF-8 编码。邮件标题、发件人和收件人可以这样设置message[Subject]Header(email_title,utf-8)message[From]self.email_sender message[To]email_send_to7.3 使用 SMTP_SSL 发送邮件withsmtplib.SMTP_SSL(self.email_host,self.email_port)assmtp:smtp.login(self.email_user,self.email_password)smtp.sendmail(self.email_sender,[email_send_to],message.as_string())这里使用SMTP_SSL连接 SMTP 服务器。常见 SSL 端口是465。其中①smtp.login()用于登录邮箱 SMTP 服务。②smtp.sendmail()用于发送邮件。③message.as_string()用于将邮件对象转换为字符串。7.4 生成验证码验证码可以使用secrets模块生成。staticmethoddefcreate_verify_code(length:int6)-str:digitsstring.digitsreturn.join(secrets.choice(digits)for_inrange(length))相比randomsecrets更适合生成验证码、Token 等安全相关随机值。8 封装验证码邮件模板下面再封装一个验证码邮件模板类。importosclassEmailTemplate:def__init__(self,template_type:strverify_code):self.template_typetemplate_typedefsend_email(self,email_send_to:str):ifself.template_typeverify_code:returnself.send_verify_code_email(email_send_to)returnFalse,0defsend_verify_code_email(self,email_send_to:str):email_clientEmailClient(email_hostos.getenv(EMAIL_HOST,smtp.163.com),email_portint(os.getenv(EMAIL_PORT,465)),email_useros.getenv(EMAIL_USER,),email_passwordos.getenv(EMAIL_PASSWORD,),email_senderos.getenv(EMAIL_SENDER,))verify_codeemail_client.create_verify_code()email_contentf div stylefont-family: Arial, sans-serif; line-height: 1.8; h2密码找回验证码/h2 p{email_send_to}您好/p p您的验证码是/p p stylefont-size: 28px; font-weight: bold; color: #2f80ed;{verify_code}/p p如果不是您本人操作请忽略此邮件并及时检查账户安全。/p /div send_stateemail_client.send_email(email_send_toemail_send_to,email_title密码找回验证码,email_contentemail_content)ifsend_state:returnTrue,verify_codereturnFalse,09 模板类说明9.1 template_typedef__init__(self,template_type:strverify_code):self.template_typetemplate_typetemplate_type用于指定邮件模板类型。当前示例只实现了验证码模板。9.2 send_emaildefsend_email(self,email_send_to:str):ifself.template_typeverify_code:returnself.send_verify_code_email(email_send_to)returnFalse,0该方法根据模板类型调用不同的邮件模板函数。返回值为①True, verify_code表示发送成功并返回验证码。②False, 0表示发送失败。9.3 send_verify_code_emaildefsend_verify_code_email(self,email_send_to:str):该方法用于生成验证码、拼接 HTML 邮件正文并调用EmailClient完成发送。10 使用示例使用前需要先配置环境变量。然后执行if__name____main__:templateEmailTemplate(template_typeverify_code)send_state,verify_codetemplate.send_email(receiverexample.com)print(发送状态,send_state)print(验证码,verify_code)如果发送成功send_state为True同时可以得到本次发送的验证码。在实际项目中验证码通常不会直接打印出来而是会保存到缓存、数据库或会话中并设置有效期。11 完整代码importosimportsecretsimportsmtplibimportstringfromemail.headerimportHeaderfromemail.mime.textimportMIMETextclassEmailClient:def__init__(self,email_host:str,email_port:int,email_user:str,email_password:str,email_sender:str):self.email_hostemail_host self.email_portemail_port self.email_useremail_user self.email_passwordemail_password self.email_senderemail_senderdefsend_email(self,email_send_to:str,email_title:str,email_content:str)-bool:messageMIMEText(email_content,html,utf-8)message[Subject]Header(email_title,utf-8)message[From]self.email_sender message[To]email_send_totry:withsmtplib.SMTP_SSL(self.email_host,self.email_port)assmtp:smtp.login(self.email_user,self.email_password)smtp.sendmail(self.email_sender,[email_send_to],message.as_string())returnTrueexceptsmtplib.SMTPExceptionaserror:print(f邮件发送失败{error})returnFalsestaticmethoddefcreate_verify_code(length:int6)-str:digitsstring.digitsreturn.join(secrets.choice(digits)for_inrange(length))classEmailTemplate:def__init__(self,template_type:strverify_code):self.template_typetemplate_typedefsend_email(self,email_send_to:str):ifself.template_typeverify_code:returnself.send_verify_code_email(email_send_to)returnFalse,0defsend_verify_code_email(self,email_send_to:str):email_clientEmailClient(email_hostos.getenv(EMAIL_HOST,smtp.163.com),email_portint(os.getenv(EMAIL_PORT,465)),email_useros.getenv(EMAIL_USER,),email_passwordos.getenv(EMAIL_PASSWORD,),email_senderos.getenv(EMAIL_SENDER,))verify_codeemail_client.create_verify_code()email_contentf div stylefont-family: Arial, sans-serif; line-height: 1.8; h2密码找回验证码/h2 p{email_send_to}您好/p p您的验证码是/p p stylefont-size: 28px; font-weight: bold; color: #2f80ed;{verify_code}/p p如果不是您本人操作请忽略此邮件并及时检查账户安全。/p /div send_stateemail_client.send_email(email_send_toemail_send_to,email_title密码找回验证码,email_contentemail_content)ifsend_state:returnTrue,verify_codereturnFalse,0if__name____main__:templateEmailTemplate(template_typeverify_code)send_state,verify_codetemplate.send_email(receiverexample.com)print(发送状态,send_state)print(验证码,verify_code)12 小结本文整理了使用 Python 发送电子邮件的基本方法。整体流程可以概括为开启邮箱 SMTP 服务获取授权码使用smtplib连接 SMTP 服务器使用MIMEText构造 HTML 邮件内容最后调用sendmail()完成发送。在实际项目中邮件发送常用于验证码、通知、告警等场景。需要特别注意的是邮箱授权码属于敏感信息不能直接写死在代码中建议通过环境变量或配置文件进行管理。