
1. 从零开始为什么选择Python发送邮件如果你还在手动一封封地点击“发送”或者觉得邮件自动化是IT部门的专属那今天这篇内容可能会改变你的看法。我最初接触用Python发邮件是因为一个很实际的需求每天需要给几十个客户发送格式雷同但数据不同的日报。手动操作不仅耗时还容易出错。当时就想能不能让电脑自己干这个活儿于是我盯上了Python。结果发现用Python的smtplib库发送邮件门槛比想象中低得多但能做的事情却非常丰富——从简单的文本通知到带附件的周报再到嵌入精美HTML模板的营销邮件它都能搞定。无论你是数据分析师想自动推送报告是运维工程师需要设置告警还是普通开发者想简化日常工作流掌握这个技能都能让你事半功倍。今天我就以一个过来人的身份带你从环境准备到实战发送一步步拆解这个过程并分享那些官方文档里不会写的“坑”和技巧。2. 环境准备与核心库smtplib初探在开始写代码之前我们需要确保“舞台”已经搭好。这里的环境准备远不止安装Python那么简单。2.1 Python环境与编辑器选择首先你需要一个可用的Python环境。从热搜词“python安装教程”、“python 3.8”可以看出很多朋友卡在了第一步。我的建议是直接去Python官网下载最新稳定版目前是3.11或3.12。安装时务必勾选“Add Python to PATH”这个选项这是避免后续在命令行中找不到python命令的关键一步。安装完成后打开终端Windows是CMD或PowerShellMac/Linux是Terminal输入python --version如果能看到版本号恭喜你第一步成功了。关于编辑器热搜里提到了VSCode和PyCharm。对于新手我强烈推荐VSCode。它轻量、免费而且通过安装Python扩展能获得代码高亮、智能提示、调试等几乎媲美IDE的功能。配置也很简单安装VSCode后在扩展市场搜索“Python”安装微软官方发布的那个即可。PyCharm功能更强大但社区版对发送邮件这种任务也完全够用看个人喜好。2.2 理解SMTP邮件是如何“寄出去”的在敲代码前我们得先搞懂邮件发送的基本原理。这就像寄信你需要知道邮局SMTP服务器在哪并且遵守邮局的规则。SMTPSimple Mail Transfer Protocol简单邮件传输协议就是互联网上的“电子邮局协议”。当你用Outlook、网页邮箱点击发送时你的邮件客户端会悄悄帮你完成以下步骤连接到你的发件邮箱服务商比如QQ邮箱、Gmail的SMTP服务器。进行身份认证输入账号密码或授权码。告诉服务器发件人、收件人、邮件内容。服务器负责将邮件路由到收件人的邮箱服务器。我们的Python脚本就是扮演了这个“邮件客户端”的角色。而smtplib库就是Python标准库中内置的、用来和SMTP服务器“对话”的工具包。你不需要额外安装它直接用import smtplib即可调用这是它最大的优势之一。2.3 获取发件邮箱的SMTP授权码这是新手最容易踩坑的地方。重要提示你不能直接用你的邮箱登录密码去连接SMTP服务器出于安全考虑几乎所有主流邮箱服务商都要求使用“授权码”或“应用专用密码”。QQ邮箱/163邮箱你需要登录网页版邮箱在“设置”-“账户”中找到“POP3/SMTP服务”或“IMAP/SMTP服务”开启它。系统会引导你生成一个16位的“授权码”。这个授权码就是你代码里要用的密码。Gmail如果你能访问Gmail需要在Google账户的“安全性”设置中开启“两步验证”然后生成“应用专用密码”。公司邮箱通常由IT部门提供SMTP服务器地址如smtp.company.com、端口和登录凭证。请务必保管好这个授权码它相当于一把专用钥匙。我们后续的代码示例将主要以QQ邮箱为例因为其在国内的普及度高流程具有代表性。3. 发送第一封纯文本邮件理论说再多不如动手试。让我们从最简单的纯文本邮件开始。3.1 基础代码框架与逐行解析下面是一个最基础的发送脚本我为你添加了详尽的注释import smtplib from email.mime.text import MIMEText from email.header import Header # 1. 配置邮箱参数请替换为你的真实信息 mail_host smtp.qq.com # QQ邮箱的SMTP服务器地址 mail_user your_emailqq.com # 你的发件邮箱地址 mail_pass your_authorization_code # 你的授权码不是登录密码 sender mail_user receivers [recipient1example.com, recipient2example.com] # 收件人列表可以是多个 # 2. 构建邮件内容 # 邮件正文 content 你好 这是一封来自Python脚本的测试邮件。 祝好 Python发信机器人 # 创建一个MIMEText对象指定内容为纯文本(plain)编码为utf-8 message MIMEText(content, plain, utf-8) # 3. 设置邮件头部信息这是邮件能正常显示的关键 message[From] Header(fPython发信助手 {sender}, utf-8) # 发件人显示名称 message[To] Header(,.join(receivers), utf-8) # 收件人 message[Subject] Header(Python SMTP 测试邮件, utf-8) # 邮件主题 # 4. 发送邮件 try: # 创建SMTP连接QQ邮箱使用SSL加密端口是465 smtpObj smtplib.SMTP_SSL(mail_host, 465) # 登录邮箱 smtpObj.login(mail_user, mail_pass) # 执行发送发件人收件人列表邮件内容已转换为字符串 smtpObj.sendmail(sender, receivers, message.as_string()) print(邮件发送成功) except smtplib.SMTPException as e: print(f邮件发送失败错误{e}) finally: # 关闭连接 smtpObj.quit()关键点解析SMTP_SSLvsSMTPQQ邮箱、Gmail等要求使用SSL加密连接端口是465所以我们用SMTP_SSL。有些公司内部服务器可能使用非加密的25端口或TLS加密的587端口那时就需要使用smtplib.SMTP()并调用starttls()方法。这是一个常见的配置坑。MIMEText这是email.mime模块下的一个类专门用于构建文本邮件。plain表示纯文本如果想发HTML邮件改为html即可。message.as_string()MIMEText对象构建的邮件内容是一个复杂的结构体不能直接发送。as_string()方法将其格式化为符合SMTP协议的标准字符串。Header类用于正确处理中文等非ASCII字符的编码问题避免主题或发件人名称出现乱码。3.2 首次运行可能遇到的错误与排查第一次运行大概率不会一帆风顺。以下是几个典型错误及解决方法smtplib.SMTPAuthenticationError: (535, bError: authentication failed)原因这是最常见的错误表示认证失败。排查99%的情况是你的mail_pass填错了。请再次确认你使用的是授权码而不是邮箱登录密码。另外检查mail_user是否填写了完整的邮箱地址。smtplib.SMTPConnectError: (421, bConnection not allowed)或 连接超时原因网络问题或者IP被邮箱服务商暂时限制。排查检查网络如果你在海外或使用特殊网络环境尝试连接QQ邮箱可能会被拒绝。可以尝试更换网络或者使用其他邮箱服务如公司邮箱。对于个人邮箱频繁测试也可能触发风控稍等片刻再试。邮件发送成功但收件箱没有/进了垃圾箱原因邮件内容被反垃圾系统拦截。排查检查邮件内容是否包含敏感词汇发件人邮箱是否是新注册或很少使用的尝试在邮件正文中加入更丰富、正常的文本内容。对于QQ邮箱可以尝试先在网页版手动给自己发一封相似内容的邮件确保发件邮箱本身信誉良好。注意请勿在公共平台或代码仓库中提交真实的邮箱地址和授权码建议使用环境变量或配置文件来管理这些敏感信息。例如可以创建一个config.py文件里面写MAIL_PASS your_code然后在主脚本中from config import MAIL_PASS并将config.py加入.gitignore文件。4. 进阶功能发送HTML邮件与附件只会发纯文本邮件实用性大打折扣。工作中我们更需要发送格式美观的报表HTML或者带上分析结果文件附件。4.1 打造精美的HTML邮件发送HTML邮件和纯文本邮件在代码结构上几乎一样核心区别在于构建MIMEText对象时将plain改为html并传入HTML格式的字符串。import smtplib from email.mime.text import MIMEText from email.header import Header mail_host smtp.qq.com mail_user your_emailqq.com mail_pass your_authorization_code sender mail_user receivers [recipientexample.com] # HTML邮件正文 html_content !DOCTYPE html html head meta charsetutf-8 style body {{ font-family: Arial, sans-serif; margin: 20px; }} .header {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }} .content {{ margin-top: 20px; line-height: 1.6; }} .highlight {{ background-color: #f8f9fa; padding: 10px; border-left: 4px solid #3498db; }} .footer {{ margin-top: 30px; font-size: 0.9em; color: #7f8c8d; text-align: center; }} /style /head body div classheader h1 每日数据报告/h1 p生成时间2023-10-27/p /div div classcontent p尊敬的同事以下是今日关键指标概览/p ul listrong网站访问量 (PV):/strong 124,567 次/li listrong独立访客 (UV):/strong 45,321 人/li listrong订单转化率:/strong span stylecolor: green;2.34%/span (较昨日上升0.12%)/li /ul div classhighlight pstrong核心发现/strong 今日通过移动端的访问占比首次突破70%建议优化移动端用户体验。/p /div /div div classfooter p此邮件由Python自动化系统发送请勿直接回复。/p p© 2023 数据团队/p /div /body /html # 关键在这里第二个参数指定为 html message MIMEText(html_content, html, utf-8) message[From] Header(f数据报告系统 {sender}, utf-8) message[To] Header(receivers[0], utf-8) message[Subject] Header(【自动化】每日业务数据报告, utf-8) # ... 发送逻辑与之前相同 ...实战心得CSS内联很多邮件客户端如Outlook、Gmail会屏蔽或重写head中的style标签。为了确保样式生效最佳实践是使用“内联CSS”即把style属性直接写在HTML标签里。虽然上面的例子用了style块为了代码清晰但在生产环境中建议使用像premailer这样的库将CSS自动内联。保持简洁邮件HTML应尽量简单避免使用复杂的JavaScript、浮动布局或外部资源图片尽量用Base64嵌入或使用稳定的图床链接因为客户端支持度差异很大。纯文本备选专业的邮件发送会同时提供HTML和纯文本版本当客户端不支持HTML时显示纯文本。这可以通过MIMEMultipart创建alternative类型实现但日常自动化中如果受众固定且支持HTML可以暂不考虑。4.2 添加附件发送数据分析结果发送附件是另一个刚需。这里我们需要引入MIMEMultipart来组合邮件正文和附件并使用MIMEApplication或MIMEText、MIMEImage等来处理不同类型的附件。import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication from email.header import Header import os mail_host smtp.qq.com mail_user your_emailqq.com mail_pass your_authorization_code sender mail_user receivers [recipientexample.com] # 1. 创建MIMEMultipart对象代表邮件本身 msg MIMEMultipart() msg[From] Header(f报告系统 {sender}, utf-8) msg[To] Header(receivers[0], utf-8) msg[Subject] Header(带附件的周报数据, utf-8) # 2. 添加邮件正文可以是文本或HTML body_text 您好本周的数据分析报告已生成详情请查看附件。 msg.attach(MIMEText(body_text, plain, utf-8)) # 3. 添加附件 # 假设我们有两个附件一个CSV报告一个PNG图表 attachments [ (weekly_report.csv, data/weekly_report.csv), # (邮件中显示的文件名, 本地文件路径) (trend_chart.png, charts/trend.png) ] for filename, filepath in attachments: if not os.path.exists(filepath): print(f警告附件文件 {filepath} 不存在跳过。) continue with open(filepath, rb) as f: file_content f.read() # 创建MIMEApplication对象指定文件内容和类型 # 使用_subtype参数或让库自动推断 att MIMEApplication(file_content) # 为附件添加头部信息指定其在邮件中的处理方式 att.add_header(Content-Disposition, attachment, filenameHeader(filename, utf-8).encode()) # 可以手动设置Content-Type但MIMEApplication通常会自动处理 # att.add_header(Content-Type, application/octet-stream, nameHeader(filename, utf-8).encode()) msg.attach(att) # 4. 发送邮件 try: smtpObj smtplib.SMTP_SSL(mail_host, 465) smtpObj.login(mail_user, mail_pass) smtpObj.sendmail(sender, receivers, msg.as_string()) print(带附件的邮件发送成功) except Exception as e: print(f发送失败: {e}) finally: smtpObj.quit()关键点与避坑指南MIMEMultipart它是邮件的“容器”可以“附着”多个部分正文、附件。MIMEApplication最适合用于二进制文件如Excel, PDF, Zip, 图片。它会自动设置Content-Type。对于纯文本附件用MIMEText也可以。文件名编码filenameHeader(filename, utf-8).encode()这行代码至关重要它能确保中文附件名在收件端正确显示而不是一堆乱码。文件路径务必使用正确的文件路径。建议使用绝对路径或者使用os.path.join()来构建与脚本位置相关的路径避免因工作目录问题找不到文件。附件大小免费邮箱服务商对单封邮件的附件总大小通常有限制如QQ邮箱是50MB。发送超大附件前请先确认限制或考虑使用云存储链接代替。5. 实战封装构建可复用的邮件发送模块当我们需要在多个脚本或项目中发送邮件时每次都复制粘贴一大段代码是低效且难以维护的。最佳实践是将邮件发送功能封装成一个独立的、可配置的模块或类。5.1 设计一个健壮的邮件发送类下面我设计一个EmailSender类它集成了我们前面讨论的所有功能并且考虑了异常处理和配置管理。# email_sender.py import smtplib import os from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication from email.header import Header from typing import List, Optional, Tuple class EmailSender: 一个通用的邮件发送类支持文本/HTML邮件和附件。 def __init__(self, host: str, port: int, user: str, password: str, use_ssl: bool True): 初始化邮件发送器。 :param host: SMTP服务器地址 :param port: SMTP服务器端口 :param user: 发件邮箱地址 :param password: 邮箱授权码 :param use_ssl: 是否使用SSL连接 (True对应465端口False对应25或587端口) self.host host self.port port self.user user self.password password self.use_ssl use_ssl self.smtp None def _connect(self): 建立与SMTP服务器的连接并登录。 try: if self.use_ssl: self.smtp smtplib.SMTP_SSL(self.host, self.port, timeout10) else: self.smtp smtplib.SMTP(self.host, self.port, timeout10) self.smtp.starttls() # 对于587端口需要启用TLS self.smtp.login(self.user, self.password) print(SMTP服务器连接登录成功。) except Exception as e: raise ConnectionError(f连接或登录SMTP服务器失败: {e}) def send(self, to_addrs: List[str], subject: str, content: str, content_type: str plain, from_name: Optional[str] None, cc_addrs: List[str] None, attachments: List[Tuple[str, str]] None) - bool: 发送邮件。 :param to_addrs: 收件人地址列表 :param subject: 邮件主题 :param content: 邮件正文内容 :param content_type: 正文类型plain 或 html :param from_name: 发件人显示名称 :param cc_addrs: 抄送人地址列表 :param attachments: 附件列表每个元素为 (邮件中显示的文件名, 本地文件路径) :return: 发送成功返回True失败返回False if not self.smtp: self._connect() # 1. 创建邮件根容器 msg MIMEMultipart() msg[From] Header(f{from_name or 自动发信} {self.user}, utf-8) msg[To] Header(,.join(to_addrs), utf-8) if cc_addrs: msg[Cc] Header(,.join(cc_addrs), utf-8) msg[Subject] Header(subject, utf-8) # 2. 添加邮件正文 msg.attach(MIMEText(content, content_type, utf-8)) # 3. 添加附件 if attachments: for display_name, file_path in attachments: if not os.path.exists(file_path): print(f附件文件不存在跳过: {file_path}) continue with open(file_path, rb) as f: file_data f.read() att MIMEApplication(file_data) att.add_header(Content-Disposition, attachment, filenameHeader(display_name, utf-8).encode()) msg.attach(att) # 4. 构建收件人列表 (To Cc) all_recipients to_addrs.copy() if cc_addrs: all_recipients.extend(cc_addrs) # 5. 发送 try: self.smtp.sendmail(self.user, all_recipients, msg.as_string()) print(f邮件发送成功至: {, .join(to_addrs)}) return True except smtplib.SMTPException as e: print(f邮件发送失败: {e}) return False def close(self): 关闭SMTP连接。 if self.smtp: try: self.smtp.quit() print(SMTP连接已关闭。) except: pass finally: self.smtp None def __enter__(self): 支持with语句上下文管理。 self._connect() return self def __exit__(self, exc_type, exc_val, exc_tb): 退出with语句块时自动关闭连接。 self.close()5.2 如何使用这个模块将上面的类保存为email_sender.py。然后在你的主程序中可以这样优雅地使用它# main.py from email_sender import EmailSender # 配置信息建议从环境变量或配置文件中读取 SMTP_CONFIG { host: smtp.qq.com, port: 465, user: your_emailqq.com, password: your_authorization_code, # 从安全位置读取 use_ssl: True } # 方法一使用with语句自动管理连接生命周期推荐 with EmailSender(**SMTP_CONFIG) as sender: # 发送纯文本邮件 sender.send( to_addrs[colleague1company.com], subject服务器状态通知, content所有服务运行正常。, content_typeplain ) # 发送带HTML和附件的邮件 html_content h1月度报告/h1p详情见附件。/p sender.send( to_addrs[managercompany.com], subject【重要】月度数据分析报告, contenthtml_content, content_typehtml, from_name数据部, attachments[ (report_october.pdf, ./reports/october.pdf), (data_summary.xlsx, ./data/summary.xlsx) ] ) # 方法二手动管理连接 # sender EmailSender(**SMTP_CONFIG) # try: # sender.send(...) # finally: # sender.close() # 确保连接被关闭封装带来的好处代码复用在任何需要发邮件的地方导入这个类即可。配置集中邮箱服务器信息在一处管理易于修改。异常安全使用with语句或try...finally确保网络连接总是被正确关闭避免资源泄漏。功能扩展后续如果想增加日志记录、重试机制、收件人分组等功能只需要在这个类里修改所有调用方都会受益。易于测试可以对这个类进行单元测试模拟SMTP服务器来验证发送逻辑。6. 高级技巧与生产环境注意事项当你把基础功能跑通后要应用到实际生产或重要任务中还需要考虑更多细节。6.1 处理收件人列表与邮件模板批量发送与隐私保护如果需要给成百上千人发送邮件切忌在To或Cc字段填入所有地址这会导致所有收件人互相可见邮箱违反隐私。正确的做法是使用“密送”Bcc。在我们的EmailSender类中可以添加一个bcc_addrs参数并在构造邮件时设置msg[Bcc]同时在sendmail函数中将密送地址也加入收件人列表。更好的方式是在循环中为每个收件人单独构建和发送邮件虽然慢但最规范。使用邮件模板对于内容固定、只有部分变量如姓名、金额需要替换的邮件应该使用模板。可以使用Python内置的string.Template或者更强大的Jinja2库。from string import Template template_str 尊敬的$name 您本月的账单金额为$$amount。 请及时处理。 template Template(template_str) personalized_content template.substitute(name张三, amount100.5) # 然后将personalized_content作为邮件正文发送6.2 错误处理、重试与日志记录生产环境的脚本必须健壮。更细致的异常捕获smtplib会抛出多种异常如SMTPConnectError,SMTPAuthenticationError,SMTPRecipientsRefused等。应该分别捕获并采取不同策略比如认证错误就立即停止连接错误可以重试。加入重试机制网络波动是常事。可以为发送操作添加一个简单的重试逻辑。import time def send_with_retry(sender, max_retries3, **kwargs): for i in range(max_retries): try: return sender.send(**kwargs) except (smtplib.SMTPConnectError, smtplib.SMTPServerDisconnected) as e: if i max_retries - 1: raise e wait_time 2 ** i # 指数退避 print(f发送失败{wait_time}秒后重试... 错误: {e}) time.sleep(wait_time)记录日志不要只用print。使用Python的logging模块将发送成功/失败、收件人、主题等信息记录到文件便于后期排查和审计。6.3 性能考量与异步发送如果需要发送大量邮件同步发送发完一封再发下一封会非常慢。此时可以考虑使用线程池concurrent.futures库的ThreadPoolExecutor可以并发发送。使用异步IO对于IO密集型的网络操作asyncioaiosmtplib库是更现代、高效的解决方案。但这会引入额外的复杂度需要根据项目需求权衡。6.4 安全与合规警告重要警告此部分内容必须严格遵守。严禁滥用绝对不要用此技术发送垃圾邮件、诈骗邮件或任何形式的恶意邮件。这不仅违反服务商条款也可能触犯法律。保护凭证邮箱授权码是最高机密。切勿写入代码提交到Git等版本控制系统。务必使用环境变量、加密的配置文件或密钥管理服务来存储。遵守频率限制所有邮件服务商都对SMTP发送有频率限制如QQ邮箱可能限制每小时或每日发送量。短时间内发送大量邮件会导致账号被暂时锁定甚至永久封禁。批量发送时务必在邮件间添加延迟如time.sleep(1)。内容合规确保邮件内容合法合规不包含敏感、欺诈或侵权信息。7. 一个完整的自动化案例定时发送系统监控报告让我们将所有知识点串联起来看一个贴近运维实际需求的例子每天上午10点自动收集服务器状态信息生成HTML报告并通过邮件发送给运维团队。# monitor_report.py import smtplib import schedule import time import psutil # 需要安装: pip install psutil from datetime import datetime from email_sender import EmailSender # 使用我们之前封装的类 import logging # 配置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(email_monitor.log), logging.StreamHandler() ]) logger logging.getLogger(__name__) # 邮件配置 (应从环境变量读取) SMTP_CONFIG { host: smtp.exmail.qq.com, # 假设使用企业邮箱 port: 465, user: monitoryourcompany.com, password: os.getenv(SMTP_PASSWORD), # 从环境变量获取密码 use_ssl: True } def collect_system_info(): 收集系统信息 cpu_percent psutil.cpu_percent(interval1) memory psutil.virtual_memory() disk psutil.disk_usage(/) boot_time datetime.fromtimestamp(psutil.boot_time()).strftime(%Y-%m-%d %H:%M:%S) info { time: datetime.now().strftime(%Y-%m-%d %H:%M:%S), cpu_usage: cpu_percent, memory_usage: memory.percent, memory_available: round(memory.available / (1024**3), 2), # 转换为GB disk_usage: disk.percent, disk_free_gb: round(disk.free / (1024**3), 2), system_boot_time: boot_time } return info def generate_html_report(info): 根据系统信息生成HTML报告 status_color green if info[cpu_usage] 80 and info[memory_usage] 85 else red status_text 正常 if status_color green else 警告 html f !DOCTYPE html html headmeta charsetutf-8title系统监控报告/title/head body stylefont-family: Segoe UI, Tahoma, Geneva, Verdana, sans-serif; h2 stylecolor: #333; border-bottom: 2px solid {status_color}; padding-bottom: 5px; ️ 服务器系统监控日报 - {info[time]} /h2 pstrong总体状态/strong span stylecolor: {status_color}; font-weight: bold;{status_text}/span/p table border1 cellspacing0 cellpadding8 styleborder-collapse: collapse; width: 100%; tr stylebackground-color: #f2f2f2; th监控项/thth当前值/thth状态/th /tr tr tdCPU使用率/tdtd{info[cpu_usage]}%/td td{✅ 正常 if info[cpu_usage] 80 else ⚠️ 偏高}/td /tr tr td内存使用率/tdtd{info[memory_usage]}%/td td{✅ 正常 if info[memory_usage] 85 else ⚠️ 偏高}/td /tr tr td可用内存/tdtd{info[memory_available]} GB/tdtd—/td /tr tr td磁盘使用率 (根目录)/tdtd{info[disk_usage]}%/td td{✅ 正常 if info[disk_usage] 90 else ⚠️ 不足}/td /tr tr td磁盘剩余空间/tdtd{info[disk_free_gb]} GB/tdtd—/td /tr tr td系统最后启动时间/tdtd colspan2{info[system_boot_time]}/td /tr /table br pem此报告由自动化监控系统生成如有异常请及时处理。/em/p /body /html return html def send_daily_report(): 执行收集、生成、发送的完整任务 logger.info(开始执行每日监控报告任务...) try: # 1. 收集信息 sys_info collect_system_info() logger.info(f系统信息收集完成: CPU {sys_info[cpu_usage]}%, Memory {sys_info[memory_usage]}%) # 2. 生成HTML内容 html_content generate_html_report(sys_info) # 3. 发送邮件 with EmailSender(**SMTP_CONFIG) as sender: success sender.send( to_addrs[ops-teamyourcompany.com], subjectf【系统监控】每日报告 {datetime.now().strftime(%Y-%m-%d)}, contenthtml_content, content_typehtml, from_name自动化监控系统 ) if success: logger.info(每日监控报告邮件发送成功。) else: logger.error(每日监控报告邮件发送失败。) except Exception as e: logger.exception(f发送每日监控报告时发生未预期错误: {e}) if __name__ __main__: logger.info(系统监控邮件服务启动。) # 使用schedule库设置定时任务每天10:00执行 schedule.every().day.at(10:00).do(send_daily_report) # 也可以立即测试一次 # send_daily_report() # 保持程序运行循环检查定时任务 while True: schedule.run_pending() time.sleep(60) # 每分钟检查一次这个案例展示了如何将邮件发送能力嵌入到一个真实的自动化工作流中。它包含了信息采集、内容生成、格式化、安全发送以及日志记录等完整环节。你可以根据实际需求修改collect_system_info函数来收集应用日志、数据库状态、业务指标等任何数据从而打造属于你自己的自动化通知系统。