1. 项目缘起为什么我们需要自动发邮件在任何一个需要与外部系统或人员交互的自动化流程里邮件通知都是一个绕不开的环节。想象一下你写了一个爬虫脚本每天凌晨定时抓取数据并生成报告难道要自己每天早起手动把报告发出去吗或者你搭建了一个服务器监控系统当CPU使用率超过90%时难道要指望运维人员24小时盯着屏幕吗显然不现实。这时候一个能够自动、可靠地发送邮件的Python脚本就成了连接自动化世界与现实通知的“信使”。我最初接触这个需求是在做一个数据备份的定时任务。脚本会在备份完成后需要将成功或失败的结果通知给相关人员。手动操作不仅低效而且容易遗忘。通过Python的smtplib和email库我实现了这个功能它稳定运行了数年成为了我工具箱里的常备利器。今天我就把这个从环境准备、原理剖析、代码实现到深度避坑的完整过程拆解给你。无论你是想给爬虫程序加个“完工通知”还是为监控脚本添上“报警铃铛”这篇详解都能让你直接“抄作业”。2. 核心原理与协议SMTP是如何工作的在动手写代码之前我们必须先搞懂邮件是怎么发出去的。这涉及到SMTP协议。你可以把它理解为邮局系统。当你寄一封信时你需要知道收件人地址并把信投递到邮筒或邮局。SMTP服务器就是那个“邮局”。SMTPSimple Mail Transfer Protocol简单邮件传输协议是一组用于从源地址到目的地址传输邮件的规则。发送邮件的过程本质上就是你的Python程序作为SMTP客户端与SMTP服务器进行一系列“对话”的过程。这个过程通常分为几个步骤建立连接你的程序连接到SMTP服务器的指定端口通常是25或加密的465/587端口。握手与认证服务器说“你好”客户端回应“你好”然后客户端需要提供用户名和密码或授权码来证明自己的身份。这就是登录邮箱账号的过程。声明发件人与收件人客户端告诉服务器“这封信来自senderexample.com要寄给receiverexample.com”。传输邮件内容客户端将邮件的所有内容包括正文、主题、附件等按照一定的格式由email库帮我们构建发送给服务器。结束会话客户端说“再见”服务器回应连接关闭。你的Python脚本扮演的是“寄信人”的角色而像QQ邮箱、163邮箱、Gmail或者你公司自己的邮件服务器它们提供了“邮局”SMTP服务器的服务。你需要知道这个“邮局”的地址SMTP服务器地址、端口号以及你的“寄信凭证”账号和密码/授权码。注意现在绝大多数公开的邮件服务商如QQ、163、Gmail都要求使用SSL或TLS加密连接并且普通密码登录通常已不可用必须使用“授权码”。这是一个非常重要的安全实践也是新手最容易卡住的第一道坎。3. 环境准备与关键配置获取你的“邮局通行证”理论懂了接下来是实战准备。你需要准备三样东西一个发件邮箱、该邮箱的SMTP服务器信息、以及一个授权码而非登录密码。我以最常用的QQ邮箱和163网易邮箱为例说明如何获取这些信息。其他邮箱服务商的流程大同小异。3.1 获取QQ邮箱的SMTP配置登录QQ邮箱网页版。点击顶部菜单栏的“设置”。选择“账户”选项卡。向下滚动找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”这一栏。你会看到“POP3/SMTP服务”和“IMAP/SMTP服务”。我们需要开启前者。点击后面的“开启”按钮。根据提示可能需要验证密保。验证成功后系统会给你一个16位的授权码。请立即复制并妥善保存这个授权码它只会显示一次这就是你代码里要用的密码。记下QQ邮箱的SMTP服务器地址smtp.qq.comSSL加密端口为465TLS加密端口为587。我们通常使用465端口。3.2 获取163邮箱的SMTP配置登录163邮箱网页版。点击页面左上角的“设置” “POP3/SMTP/IMAP”。勾选“开启POP3服务”或“开启IMAP/SMTP服务”。同样根据提示进行安全验证。系统会给你一个授权码注意不是你的邮箱登录密码。保存好。记下163邮箱的SMTP服务器地址smtp.163.comSSL端口465 TLS端口587。为什么用授权码这是为了安全。如果你直接在代码里写入邮箱登录密码一旦代码泄露你的邮箱就完全暴露了。而授权码可以单独为“第三方应用”生成即使泄露你也可以随时在邮箱设置中将其禁用而不影响邮箱主密码的安全。准备好这些信息后我们得到如下关键配置字典请替换为你自己的信息# 配置信息 (请替换为你的实际信息) mail_config { “qq”: { “smtp_server”: “smtp.qq.com”, “smtp_port”: 465, # SSL端口 “sender_email”: “your_qqqq.com”, # 你的QQ邮箱 “password”: “your_authorization_code_here”, # 你的QQ邮箱授权码不是密码 }, “163”: { “smtp_server”: “smtp.163.com”, “smtp_port”: 465, “sender_email”: “your_163163.com”, “password”: “your_163_authorization_code_here”, } } # 收件人列表可以是多个 receiver_emails [“receiver1example.com”, “receiver2example.com”]4. 基础实战发送一封纯文本邮件现在我们开始编写第一个脚本。我们将使用smtplib库来处理SMTP连接和发送使用email库来构建一封格式正确的邮件。4.1 构建邮件内容MIME格式邮件不是简单的字符串它有一套复杂的MIME格式标准用来定义主题、发件人、收件人、正文类型纯文本、HTML、附件等。email.mime.text模块下的MIMEText类可以帮助我们轻松创建一封文本邮件。from email.mime.text import MIMEText from email.header import Header def create_text_email(sender, receivers, subject, content): “”” 创建一封纯文本邮件 :param sender: 发件人邮箱 :param receivers: 收件人邮箱列表 :param subject: 邮件主题 :param content: 邮件正文内容 :return: 构建好的MIMEText对象 “”” # 创建邮件对象指定内容为纯文本‘plain’编码为utf-8以支持中文 message MIMEText(content, ‘plain’, ‘utf-8’) # 设置邮件头部信息 message[‘From’] Header(f”发件人昵称 {sender}“, ‘utf-8’) # 可以自定义发件人显示名称 message[‘To’] Header(“, “.join(receivers), ‘utf-8’) # 多个收件人用逗号分隔 message[‘Subject’] Header(subject, ‘utf-8’) # 主题 return message关键点解析MIMEText(content, ‘plain’, ‘utf-8’)‘plain’表示这是纯文本。如果你想发送HTML格式的邮件只需将‘plain’改为‘html’并将content变量赋值为HTML字符串即可。‘utf-8’编码确保了中文字符不会乱码。头部字段From,To,Subject必须按照标准设置。Header类用于正确处理包含非ASCII字符如中文的头部信息。4.2 建立SMTP连接并发送有了邮件内容对象接下来就是通过SMTP协议把它送出去。import smtplib from email.mime.text import MIMEText from email.header import Header def send_email_smtp_ssl(config, receiver_emails, subject, content): “”” 使用SSL加密方式发送邮件推荐更简单 :param config: 包含smtp_server, smtp_port, sender_email, password的字典 :param receiver_emails: 收件人列表 :param subject: 邮件主题 :param content: 邮件正文 “”” # 1. 构建邮件 message create_text_email(config[“sender_email”], receiver_emails, subject, content) try: # 2. 创建SMTP_SSL对象建立安全连接 # 在创建对象时直接传入服务器和端口它会自动进行SSL握手 with smtplib.SMTP_SSL(config[“smtp_server”], config[“smtp_port”]) as server: # 3. 登录邮箱账户 server.login(config[“sender_email”], config[“password”]) print(“SMTP服务器登录成功”) # 4. 发送邮件 # sendmail 参数发件人收件人列表字符串邮件内容as_string()方法将对象转为标准邮件文本 server.sendmail(config[“sender_email”], receiver_emails, message.as_string()) print(f”邮件发送成功收件人{‘ ‘.join(receiver_emails)}“) except smtplib.SMTPAuthenticationError: print(“错误认证失败。请检查邮箱地址和授权码不是密码是否正确。”) except smtplib.SMTPException as e: print(f”SMTP错误发生{e}“) except Exception as e: print(f”其他错误{e}“) # 使用示例 config mail_config[“qq”] # 使用QQ邮箱配置 subject “【Python自动化测试】纯文本邮件” content “”” 这是一封来自Python自动化脚本的测试邮件。 此邮件用于验证SMTP发送功能是否正常。 当前时间可以在这里用datetime模块插入时间。 “”” send_email_smtp_ssl(config, receiver_emails, subject, content)代码逻辑与避坑点SMTP_SSLvsSMTPSMTP_SSL在连接建立时就进行SSL加密适用于465端口代码更简洁。如果你的服务器使用587端口STARTTLS则需要使用SMTP类然后调用starttls()方法。代码略有不同但逻辑相似。with上下文管理器使用with语句可以确保连接在使用完毕后被正确关闭即使发生异常也是如此。这是一个好习惯。login方法这里填入的password必须是前面获取的授权码而不是你的邮箱登录密码。90%的初次失败都是因为这里填错了。sendmail方法注意第二个参数是收件人列表。message.as_string()是将我们构建好的MIME对象转换成符合SMTP协议的标准字符串格式。异常处理我们特别捕获了SMTPAuthenticationError这是认证失败的专用异常可以给用户更清晰的错误提示。其他SMTP相关异常用SMTPException捕获。5. 功能进阶发送HTML邮件与带附件的邮件只会发纯文本邮件显然不够用。工作报告、数据报表、系统通知往往需要更丰富的格式和内容。5.1 发送HTML格式邮件发送HTML邮件非常简单只需在创建MIMEText对象时将第二个参数从‘plain’改为‘html’。from email.mime.text import MIMEText def create_html_email(sender, receivers, subject, html_content): “”” 创建一封HTML格式邮件 :param html_content: HTML格式的字符串 “”” message MIMEText(html_content, ‘html’, ‘utf-8’) message[‘From’] Header(f”系统通知 {sender}“, ‘utf-8’) message[‘To’] Header(“, “.join(receivers), ‘utf-8’) message[‘Subject’] Header(subject, ‘utf-8’) return message # HTML内容示例 html_content “”” !DOCTYPE html html head meta charset”utf-8 title系统日报/title style body {{ font-family: Arial, sans-serif; }} .header {{ color: #333366; border-bottom: 2px solid #333366; }} .metric {{ background-color: #f0f0f0; padding: 10px; margin: 5px 0; border-radius: 5px; }} .good {{ color: green; font-weight: bold; }} .warning {{ color: orange; font-weight: bold; }} /style /head body h1 class”header” 每日系统监控报告/h1 p生成时间2023-10-27 08:00:00/p div class”metric” spanCPU平均使用率/spanspan class”good”65%/span /div div class”metric” span内存使用率/spanspan class”warning”85%/span /div div class”metric” span磁盘空间剩余/spanspan class”good”120GB/span /div p详情请登录a href”http://your-monitor-system.com”监控平台/a查看。/p /body /html “””.format() # 这里可以用变量动态填充内容 # 发送函数和之前一样只需传入html_content和调用create_html_email config mail_config[“163”] subject_html “【系统监控】HTML日报” send_email_smtp_ssl(config, receiver_emails, subject_html, html_content)实战心得HTML邮件的美观度很大程度上取决于CSS的支持情况。不同的邮件客户端如Outlook、Gmail、手机邮箱App对CSS的支持差异巨大。为了最好的兼容性建议使用内联样式style””属性并尽量使用简单的表格布局。复杂的Flexbox或Grid布局可能在很多客户端中显示错乱。5.2 发送带附件的邮件发送附件需要用到email.mime.multipart模块中的MIMEMultipart类。它就像一个“容器”可以把文本正文和多个附件“装”在一起。from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication import os def create_email_with_attachment(sender, receivers, subject, text_content, attachment_paths): “”” 创建带附件的邮件 :param text_content: 正文文本可以是纯文本或HTML :param attachment_paths: 附件文件路径列表 “”” # 创建一个混合类型的MIME对象可以包含多种内容 message MIMEMultipart() message[‘From’] Header(f”数据报告 {sender}“, ‘utf-8’) message[‘To’] Header(“, “.join(receivers), ‘utf-8’) message[‘Subject’] Header(subject, ‘utf-8’) # 1. 添加邮件正文 # 这里我们添加一个纯文本正文你也可以添加一个HTML正文或者两者都加 text_part MIMEText(text_content, ‘plain’, ‘utf-8’) message.attach(text_part) # 2. 循环添加所有附件 for file_path in attachment_paths: if not os.path.isfile(file_path): print(f”警告附件文件不存在已跳过。{file_path}“) continue # 获取文件名 file_name os.path.basename(file_path) # 以二进制模式读取文件 with open(file_path, ‘rb’) as f: file_data f.read() # 创建MIMEApplication对象来表示附件 # MIMEApplication默认用于二进制数据流 attachment MIMEApplication(file_data, Namefile_name) # 设置附件的头部信息告诉邮件客户端这是一个附件并指定文件名 # RFC 2231 编码用于处理包含非ASCII字符的文件名 attachment.add_header(‘Content-Disposition’, ‘attachment’, filenameHeader(file_name, ‘utf-8’).encode()) # 将附件添加到邮件容器中 message.attach(attachment) print(f”已添加附件{file_name}“) return message # 使用示例 config mail_config[“qq”] subject_attach “【数据导出】带附件的报告” text_content_attach “您好\n\n本次数据导出的报告文件详见附件请查收。\n\n此致\n自动化脚本” # 假设当前目录下有这两个文件 attachment_files [“monthly_report.pdf”, “data_export.xlsx”] message_with_attachments create_email_with_attachment( config[“sender_email”], receiver_emails, subject_attach, text_content_attach, attachment_files ) # 发送函数保持不变传入构建好的message对象 try: with smtplib.SMTP_SSL(config[“smtp_server”], config[“smtp_port”]) as server: server.login(config[“sender_email”], config[“password”]) server.sendmail(config[“sender_email”], receiver_emails, message_with_attachments.as_string()) print(“带附件的邮件发送成功”) except Exception as e: print(f”发送失败{e}“)关键技术细节MIMEMultipart()创建了一个多部分邮件对象。‘mixed’是默认的子类型表示包含独立部分如正文和附件。如果正文是HTML和纯文本的替代版本可以使用‘alternative’子类型。attach()方法用于将各个MIME部分正文、附件添加到主容器中。附件文件名编码Header(file_name, ‘utf-8’).encode()这行代码至关重要。它确保了包含中文等字符的文件名在邮件客户端中能正确显示而不是变成一堆乱码。这是处理附件时的一个经典坑点。文件存在性检查在尝试打开文件前进行检查可以避免因文件路径错误导致整个脚本崩溃。6. 生产环境实战封装、调度与异常处理一个能在生产环境稳定运行的自动发邮件脚本绝不仅仅是把上面的代码跑通就行。它需要健壮性、可配置性和可维护性。下面我分享几个在实际项目中积累的关键经验。6.1 封装成可配置的邮件发送类将邮件发送功能封装成一个类便于管理配置和复用。import smtplib import os from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.header import Header from typing import List, Optional class EmailSender: “””一个健壮的邮件发送器类“”” def __init__(self, smtp_server: str, smtp_port: int, sender_email: str, password: str, use_ssl: bool True): “”” 初始化邮件发送器 :param use_ssl: True表示使用SMTP_SSL (端口465)False表示使用SMTPSTARTTLS (端口587) “”” self.smtp_server smtp_server self.smtp_port smtp_port self.sender_email sender_email self.password password self.use_ssl use_ssl self.server None def _connect_and_login(self): “””建立连接并登录内部方法“”” try: if self.use_ssl: self.server smtplib.SMTP_SSL(self.smtp_server, self.smtp_port, timeout30) else: self.server smtplib.SMTP(self.smtp_server, self.smtp_port, timeout30) self.server.starttls() # 升级为TLS加密连接 self.server.login(self.sender_email, self.password) print(f”已成功连接到 {self.smtp_server}:{self.smtp_port}“) return True except smtplib.SMTPAuthenticationError: print(“错误SMTP认证失败。请检查邮箱和授权码。”) return False except (smtplib.SMTPException, ConnectionRefusedError, TimeoutError) as e: print(f”网络或SMTP连接错误{e}“) return False def send(self, receivers: List[str], subject: str, content: str, content_type: str ‘plain’, attachments: Optional[List[str]] None) - bool: “”” 发送邮件的主方法 :param receivers: 收件人列表 :param subject: 主题 :param content: 正文内容 :param content_type: ‘plain’ 或 ‘html’ :param attachments: 附件文件路径列表 :return: 发送成功返回True失败返回False “”” if not receivers: print(“错误收件人列表为空。”) return False # 1. 构建邮件 if attachments: message self._create_multipart_message(receivers, subject, content, content_type, attachments) else: message self._create_simple_message(receivers, subject, content, content_type) if not message: return False # 2. 连接并发送 if not self._connect_and_login(): return False try: self.server.sendmail(self.sender_email, receivers, message.as_string()) print(f”邮件发送成功至 {‘ ‘.join(receivers)}“) return True except smtplib.SMTPRecipientsRefused as e: print(f”错误收件人被拒绝。详情{e}“) return False except smtplib.SMTPSenderRefused as e: print(f”错误发件人被拒绝。详情{e}“) return False except smtplib.SMTPDataError as e: print(f”错误服务器拒绝邮件数据。可能内容被判定为垃圾邮件。详情{e}“) return False except Exception as e: print(f”发送过程中发生未知错误{e}“) return False finally: if self.server: try: self.server.quit() except: pass self.server None def _create_simple_message(self, receivers, subject, content, content_type): “””创建简单邮件无附件“”” try: message MIMEText(content, content_type, ‘utf-8’) message[‘From’] Header(f”自动发送系统 {self.sender_email}“, ‘utf-8’) message[‘To’] Header(“, “.join(receivers), ‘utf-8’) message[‘Subject’] Header(subject, ‘utf-8’) return message except Exception as e: print(f”构建邮件内容时出错{e}“) return None def _create_multipart_message(self, receivers, subject, content, content_type, attachments): “””创建带附件的复杂邮件“”” try: message MIMEMultipart() message[‘From’] Header(f”自动发送系统 {self.sender_email}“, ‘utf-8’) message[‘To’] Header(“, “.join(receivers), ‘utf-8’) message[‘Subject’] Header(subject, ‘utf-8’) # 添加正文 text_part MIMEText(content, content_type, ‘utf-8’) message.attach(text_part) # 添加附件 for file_path in attachments: if os.path.isfile(file_path): self._attach_file(message, file_path) else: print(f”警告附件文件不存在已跳过。{file_path}“) return message except Exception as e: print(f”构建带附件邮件时出错{e}“) return None def _attach_file(self, message, file_path): “””将单个文件添加为附件“”” file_name os.path.basename(file_path) with open(file_path, ‘rb’) as f: part MIMEApplication(f.read(), Namefile_name) part.add_header(‘Content-Disposition’, ‘attachment’, filenameHeader(file_name, ‘utf-8’).encode()) message.attach(part) # 使用示例 if __name__ “__main__”: # 从配置文件或环境变量读取敏感信息不要硬编码在代码里 sender EmailSender( smtp_server”smtp.qq.com”, smtp_port465, sender_email”your_qqqq.com”, password”your_auth_code”, # 建议从环境变量读取 use_sslTrue ) success sender.send( receivers[“colleaguecompany.com”], subject”自动化任务完成通知”, content”您的数据处理任务已于 $(date) 成功完成。”, # 实际使用时用datetime替换 content_type‘plain’, attachments[“output/result.csv”] ) if success: print(“主流程邮件通知已发送。”) else: print(“主流程邮件发送失败需要记录日志或触发其他告警。”)这个类的设计考量分离构建与发送_create_*方法负责构建MIME对象_connect_and_login和send负责网络操作职责清晰。详尽的异常处理针对SMTP协议的各种特定异常如收件人拒绝、发件人拒绝、数据错误进行了分别捕获能提供更精准的错误信息便于排查。资源管理在finally块中确保quit()被调用释放服务器连接。可扩展性如果需要添加CC抄送、BCC密送功能只需在构建消息时添加相应的头部字段即可。6.2 与定时任务结合Cron, APScheduler自动发送邮件通常意味着定时发送。在Linux服务器上最经典的方式是使用Cron。假设你的脚本保存为send_report.py并且已经配置好了类EmailSender。编写可执行的Python脚本确保脚本开头有#!/usr/bin/env python3并赋予执行权限 (chmod x send_report.py)。使用Cron定时调度# 打开当前用户的cron编辑模式 crontab -e添加一行例如每天上午9点执行0 9 * * * /usr/bin/python3 /path/to/your/send_report.py /path/to/log/email_sender.log 210 9 * * *表示分钟(0)小时(9)每天。 ...log 21将脚本的标准输出和错误输出都重定向到日志文件便于后期排查问题。如果你需要在Python程序内部进行更复杂的调度比如每隔一段时间或者在某个条件触发后可以使用APScheduler这样的库。from apscheduler.schedulers.blocking import BlockingScheduler from datetime import datetime # 假设上面定义的 EmailSender 类可用 def job_function(): “””定时执行的任务“”” print(f”[{datetime.now()}] 开始执行定时邮件发送任务...”) sender EmailSender(...) # 初始化你的发送器 # ... 准备邮件内容和附件 ... success sender.send(...) if not success: # 发送失败可以记录到更详细的日志或触发其他告警如发短信 print(f”[{datetime.now()}] 警告邮件发送失败”) if __name__ “__main__”: scheduler BlockingScheduler() # 每天上午10:30执行 scheduler.add_job(job_function, ‘cron’, hour10, minute30) # 或者每2小时执行一次 # scheduler.add_job(job_function, ‘interval’, hours2) print(“定时邮件发送服务已启动按 CtrlC 退出。”) try: scheduler.start() except (KeyboardInterrupt, SystemExit): print(“\n服务已停止。”)6.3 生产环境避坑大全以下是我在多年运维自动化邮件通知系统中踩过的坑和总结的经验授权码过期或错误这是最常见的问题。授权码可能过期如QQ邮箱的授权码有时效或者复制时包含了空格。错误信息通常是SMTPAuthenticationError。定期检查并更新授权码。被识别为垃圾邮件内容避免使用过于营销化的词汇如“免费”、“赢取”、“点击这里”减少感叹号正文中最好包含收件人的称呼。频率短时间内向大量地址发送相同内容极易被判定为垃圾邮件。对于群发考虑使用邮件群发服务如SendGrid, Amazon SES而非个人SMTP。发件人信誉长期使用一个固定发件邮箱并保持良好发送记录有助于提升信誉。附件大小限制大多数免费邮箱服务商对附件有大小限制如QQ邮箱50MB163邮箱50MB。发送大文件前先检查或考虑使用云存储链接代替附件。连接超时与重试机制网络不稳定可能导致连接超时。在生产代码中应该为关键的网络操作如login,sendmail添加重试逻辑。import time def send_with_retry(email_sender, max_retries3): for i in range(max_retries): try: return email_sender.send(...) except (smtplib.SMTPException, TimeoutError) as e: if i max_retries - 1: raise # 最后一次重试失败抛出异常 wait_time 2 ** i # 指数退避 print(f”发送失败{wait_time}秒后重试 ({i1}/{max_retries})。错误{e}“) time.sleep(wait_time)编码问题确保整个流程脚本文件、字符串处理、邮件头都使用UTF-8编码。特别是在处理中文文件名和主题时Header类是必须的。密码等敏感信息存储绝对不要将邮箱密码或授权码硬编码在脚本中应该使用环境变量或配置文件并在版本控制中忽略这些文件。# 在shell中设置环境变量 export EMAIL_PASSWORD‘your_auth_code_here’# 在Python中读取 import os password os.environ.get(‘EMAIL_PASSWORD’) if not password: raise ValueError(“请设置 EMAIL_PASSWORD 环境变量”)日志记录完善的日志对于排查问题至关重要。记录每次发送的时间、收件人、主题、成功与否、错误信息等。可以使用Python标准的logging模块。7. 场景扩展在常见自动化项目中的应用掌握了核心技能后我们来看看如何将它嵌入到具体的自动化场景中让脚本真正“活”起来。场景一爬虫任务完成通知你的爬虫在深夜运行抓取了最新数据。你希望早上一起床就能在邮箱里看到结果摘要和可能遇到的错误。# 伪代码示例 def run_spider_and_notify(): try: data, stats, errors run_my_spider() # 分析结果生成报告内容 if errors: content f”爬虫运行完成但遇到 {len(errors)} 个错误。\n详情\n” “\n”.join(errors) subject “【爬虫警报】运行完成但有错误” else: content f”爬虫成功运行共抓取 {stats[‘count’]} 条数据。” subject “【爬虫通知】任务成功完成” # 可以附加日志文件 attachments [“spider.log”] if os.path.exists(“spider.log”) else None email_sender.send([“yourselfemail.com”], subject, content, attachmentsattachments) except Exception as e: # 爬虫本身崩溃了 email_sender.send([“yourselfemail.com”], “【爬虫严重错误】脚本崩溃”, f”错误信息{str(e)}“)场景二服务器监控报警监控脚本定期检查服务器指标一旦超过阈值就触发报警邮件。import psutil # 需要安装 psutil 库 def check_server_and_alert(): cpu_percent psutil.cpu_percent(interval1) memory psutil.virtual_memory() disk psutil.disk_usage(‘/’) alerts [] if cpu_percent 80: alerts.append(f”CPU使用率过高{cpu_percent}%“) if memory.percent 90: alerts.append(f”内存使用率过高{memory.percent}%“) if disk.percent 85: alerts.append(f”磁盘空间不足剩余 {disk.free / (1024**3):.2f} GB”) if alerts: subject “【服务器监控警报】” content “\n”.join([“检测到以下问题”] alerts [“\n请及时处理”]) # 可以发送给运维团队邮箱列表 email_sender.send([“ops-teamcompany.com”, “admincompany.com”], subject, content)场景三自动化报表推送使用pandas处理数据生成Excel或PDF报告然后通过邮件定时推送给相关人员。import pandas as pd from datetime import datetime, timedelta def generate_and_send_daily_report(): # 1. 从数据库或API获取数据 # df pd.read_sql_query(“SELECT * FROM sales WHERE date …”, connection) # 这里用模拟数据 df pd.DataFrame({‘产品’: [‘A’ ‘B’ ‘C’], ‘销售额’: [1000, 1500, 800]}) # 2. 生成Excel文件 report_date (datetime.now() - timedelta(days1)).strftime(‘%Y-%m-%d’) excel_filename f”sales_report_{report_date}.xlsx” df.to_excel(excel_filename, indexFalse) # 3. 生成邮件正文HTML格式更美观 html_content f””” h2每日销售报表 ({report_date})/h2 p总销售额strong{df[‘销售额’].sum()}/strong/p p详情请查看附件Excel文件。/p “”” # 4. 发送 email_sender.send( receivers[“managercompany.com”, “salescompany.com”], subjectf”每日销售报表 - {report_date}“, contenthtml_content, content_type‘html’, attachments[excel_filename] ) # 5. 可选发送后删除本地临时文件 os.remove(excel_filename)通过这些场景你可以看到自动发送邮件不是一个孤立的功能而是一个能够将各种自动化任务的结果“告知”现实世界的桥梁。它的稳定性和可靠性直接影响到整个自动化流程的闭环质量。花时间把邮件发送模块封装好、测试充分在未来的各种项目中都会让你事半功倍。