进阶向:Python编写自动化邮件发送程序

Python编写自动化邮件发送程序:从零开始详解

在数字化时代,自动化邮件发送功能已成为企业和个人提升工作效率的重要工具。据统计,全球每天发送的商业邮件超过30亿封,其中约40%是通过自动化系统发送的。这种功能被广泛应用于多种场景:在日常办公中用于自动发送会议通知和日程提醒;在电商领域用于订单确认和物流跟踪;在营销推广中用于EDM邮件营销和客户关怀;在IT系统中用于发送告警通知和验证码等。

Python凭借其简洁优雅的语法和强大的生态系统,成为实现邮件自动化的首选编程语言。其标准库中的smtplib和email模块提供了完整的邮件处理功能,而第三方库如yagmail更是将发送过程简化到极致。Python的跨平台特性使其可以在Windows、Linux和macOS等不同操作系统上稳定运行,且能轻松与其他系统集成。

本教程将详细介绍如何用Python编写一个完整的自动化邮件发送程序,包括:

  1. 配置SMTP服务器参数
  2. 构建邮件内容(支持纯文本和HTML格式)
  3. 添加附件(如图片、文档等)
  4. 实现批量发送功能
  5. 处理发送异常和日志记录
  6. 部署到生产环境的注意事项

我们将通过实际案例演示,从基础的单封邮件发送到高级的定时批量发送功能,帮助读者掌握完整的邮件自动化解决方案。


理解SMTP协议基础

SMTP(Simple Mail Transfer Protocol)是用于发送电子邮件的标准协议。Python通过smtplib库提供SMTP协议支持。发送邮件的基本流程涉及三个关键环节:建立SMTP连接、进行身份验证、构造并发送邮件内容。

电子邮件通常由头部(From/To/Subject等)和正文组成,可能包含纯文本、HTML内容或附件。MIME(Multipurpose Internet Mail Extensions)标准用于扩展邮件格式支持。


配置开发环境

需要安装Python 3.6及以上版本。通过以下命令安装必要依赖库:

pip install secure-smtplib email-validator

建议使用虚拟环境隔离项目依赖:

python -m venv email_env
source email_env/bin/activate  # Linux/macOS
email_env\Scripts\activate     # Windows


构建邮件发送函数核心逻辑

创建send_email.py文件,导入基础模块:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.utils import formatdate
import getpass

初始化SMTP连接函数:

def connect_smtp_server(smtp_server, port, username, password):try:server = smtplib.SMTP(smtp_server, port)server.starttls()server.login(username, password)return serverexcept Exception as e:print(f"连接SMTP服务器失败: {str(e)}")raise

邮件内容构造器:

def build_email(sender, receiver, subject, body, body_type='plain'):message = MIMEMultipart()message['From'] = sendermessage['To'] = receivermessage['Date'] = formatdate(localtime=True)message['Subject'] = subjectif body_type == 'html':content = MIMEText(body, 'html')else:content = MIMEText(body, 'plain')message.attach(content)return message


实现完整发送流程

主发送函数整合所有组件:

def send_email(smtp_config, email_content):try:server = connect_smtp_server(smtp_config['server'],smtp_config['port'],smtp_config['username'],smtp_config['password'])message = build_email(email_content['sender'],email_content['receiver'],email_content['subject'],email_content['body'],email_content.get('body_type', 'plain'))server.sendmail(email_content['sender'],email_content['receiver'],message.as_string())server.quit()print("邮件发送成功")return Trueexcept Exception as e:print(f"邮件发送失败: {str(e)}")return False


添加附件支持功能

扩展邮件构造器支持附件:

from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
import osdef add_attachment(message, file_path):with open(file_path, "rb") as attachment:part = MIMEBase('application', 'octet-stream')part.set_payload(attachment.read())encoders.encode_base64(part)part.add_header('Content-Disposition',f'attachment; filename={os.path.basename(file_path)}')message.attach(part)

更新后的邮件构造器:

def build_email(sender, receiver, subject, body, attachments=None, body_type='plain'):message = MIMEMultipart()# ... 原有头部设置代码 ...if attachments:for file_path in attachments:if os.path.exists(file_path):add_attachment(message, file_path)else:print(f"警告:附件文件不存在 {file_path}")return message


实现HTML邮件模板

创建HTML模板文件template.html

<!DOCTYPE html>
<html>
<head><style>body { font-family: Arial, sans-serif; }.header { color: #2c3e50; }.content { margin: 20px 0; }.footer { color: #7f8c8d; font-size: 0.8em; }</style>
</head>
<body><h1 class="header">${HEADER}</h1><div class="content">${CONTENT}</div><div class="footer">自动发送于 ${DATE}</div>
</body>
</html>

添加模板处理函数:

from string import Template
import datetimedef render_template(template_file, **kwargs):with open(template_file, 'r') as f:template = Template(f.read())if 'DATE' not in kwargs:kwargs['DATE'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')return template.substitute(**kwargs)


安全增强措施

实现密码加密存储:

from cryptography.fernet import Fernet
import base64
import jsonclass CredentialManager:def __init__(self, key_file='.key'):self.key = self._load_or_generate_key(key_file)def _load_or_generate_key(self, key_file):if os.path.exists(key_file):with open(key_file, 'rb') as f:return f.read()else:key = Fernet.generate_key()with open(key_file, 'wb') as f:f.write(key)return keydef save_credentials(self, config_file, credentials):cipher = Fernet(self.key)encrypted = cipher.encrypt(json.dumps(credentials).encode())with open(config_file, 'wb') as f:f.write(encrypted)def load_credentials(self, config_file):cipher = Fernet(self.key)with open(config_file, 'rb') as f:encrypted = f.read()return json.loads(cipher.decrypt(encrypted).decode())


完整实现代码

# email_sender.py
import smtplib
import os
import json
import getpass
import datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formatdate
from string import Template
from cryptography.fernet import Fernet
import base64class EmailSender:def __init__(self, config_file='config.json', key_file='.key'):self.config_file = config_fileself.cred_manager = CredentialManager(key_file)if not os.path.exists(config_file):self._setup_config()self.config = self.cred_manager.load_credentials(config_file)def _setup_config(self):print("首次使用需要进行配置")smtp_server = input("SMTP服务器地址 (如smtp.example.com): ")port = int(input("端口号 (587 for TLS): "))username = input("邮箱地址: ")password = getpass.getpass("邮箱密码/授权码: ")credentials = {'smtp_server': smtp_server,'port': port,'username': username,'password': password}self.cred_manager.save_credentials(self.config_file, credentials)def connect_smtp(self):try:server = smtplib.SMTP(self.config['smtp_server'], self.config['port'])server.starttls()server.login(self.config['username'], self.config['password'])return serverexcept Exception as e:raise Exception(f"SMTP连接失败: {str(e)}")def build_message(self, to, subject, body, body_type='plain', attachments=None, cc=None, bcc=None):msg = MIMEMultipart()msg['From'] = self.config['username']msg['To'] = tomsg['Subject'] = subjectmsg['Date'] = formatdate(localtime=True)if cc:msg['Cc'] = ccif bcc:msg['Bcc'] = bccif body_type == 'html':msg.attach(MIMEText(body, 'html'))else:msg.attach(MIMEText(body, 'plain'))if attachments:for file_path in attachments:if os.path.exists(file_path):self._add_attachment(msg, file_path)else:print(f"附件不存在: {file_path}")return msgdef _add_attachment(self, message, file_path):with open(file_path, "rb") as f:part = MIMEBase('application', 'octet-stream')part.set_payload(f.read())encoders.encode_base64(part)part.add_header('Content-Disposition',f'attachment; filename="{os.path.basename(file_path)}"')message.attach(part)def send(self, to, subject, body, body_type='plain', attachments=None, cc=None, bcc=None):try:server = self.connect_smtp()recipients = [to]if cc:recipients += cc.split(',')if bcc:recipients += bcc.split(',')msg = self.build_message(to, subject, body, body_type, attachments, cc, bcc)server.sendmail(self.config['username'], recipients, msg.as_string())server.quit()return Trueexcept Exception as e:print(f"发送失败: {str(e)}")return Falseclass CredentialManager:def __init__(self, key_file='.key'):self.key = self._load_or_generate_key(key_file)def _load_or_generate_key(self, key_file):if os.path.exists(key_file):with open(key_file, 'rb') as f:return f.read()else:key = Fernet.generate_key()with open(key_file, 'wb') as f:f.write(key)return keydef save_credentials(self, config_file, credentials):cipher = Fernet(self.key)encrypted = cipher.encrypt(json.dumps(credentials).encode())with open(config_file, 'wb') as f:f.write(encrypted)def load_credentials(self, config_file):cipher = Fernet(self.key)with open(config_file, 'rb') as f:encrypted = f.read()return json.loads(cipher.decrypt(encrypted).decode())def render_template(template_file, **kwargs):with open(template_file, 'r') as f:template = Template(f.read())if 'DATE' not in kwargs:kwargs['DATE'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')return template.substitute(**kwargs)if __name__ == "__main__":sender = EmailSender()# 示例1:发送纯文本邮件sender.send(to="recipient@example.com",subject="测试纯文本邮件",body="这是一封测试邮件的正文内容。")# 示例2:发送带附件的HTML邮件html_content = """<h1>HTML邮件测试</h1><p>这是一封<strong>HTML格式</strong>的测试邮件。</p><p>当前时间:{}</p>""".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))sender.send(to="recipient@example.com",subject="测试HTML邮件",body=html_content,body_type='html',attachments=['report.pdf', 'data.xlsx'])# 示例3:使用模板发送邮件template_vars = {'HEADER': '季度报告','CONTENT': '请查收附件中的季度财务报告。'}rendered = render_template('template.html', **template_vars)sender.send(to="manager@example.com",subject="季度财务报告",body=rendered,body_type='html',attachments=['quarterly_report.pdf'])


项目结构说明

完整项目应包含以下文件:

/email_project
│── email_sender.py       # 主程序
│── config.json           # 加密的配置文件
│── .key                  # 加密密钥
│── template.html         # HTML模板
├── attachments/          # 附件目录
│   ├── report.pdf
│   └── data.xlsx
└── requirements.txt      # 依赖列表

requirements.txt内容:

secure-smtplib==1.0.0
cryptography==39.0.1


部署与使用指南

  1. 首次运行会自动引导配置SMTP参数
  2. 支持三种发送模式:
    • 纯文本邮件
    • HTML格式邮件
    • 带附件邮件
  3. 密码等敏感信息采用AES加密存储
  4. 可通过继承EmailSender类扩展功能

安全注意事项

  1. 不要将.key文件提交到版本控制
  2. 建议使用应用专用密码而非账户密码
  3. 附件发送前应进行病毒扫描
  4. 生产环境建议添加发送频率限制

通过本教程,可以构建一个功能完备的企业级邮件自动发送系统。根据实际需求,可进一步扩展邮件队列、发送状态跟踪等功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95384.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95384.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGpt 5系列文章1——编码与智能体

人工智能技术正在以惊人的速度发展&#xff0c;重新定义着开发人员的工作方式。2025年8月&#xff0c;OpenAI正式发布了面向开发人员的GPT-5 一、GPT-5的编码能力突破 GPT-5在关键编码基准测试中创造了行业新纪录(SOTA)&#xff0c;在SWE-bench Verified测试中得分74.9%&…

力扣top100(day02-05)--二叉树 02

102. 二叉树的层序遍历 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right)…

开疆智能Ethernet转ModbusTCP网关连接发那科机器人与三菱PLC配置案例

本案例是三菱FX5U PLC通过ethernet/IP转ModbusTCP网关对发那科机器人进行控制的配置案例。PLC端主要配置以太网端口设置在通信测试中&#xff0c;PLC作为主站&#xff0c;在PLC设置中选择“以太网端口”非常关键&#xff0c;以确保通信测试的正常进行。1、首先&#xff0c;在PL…

VUE+SPRINGBOOT从0-1打造前后端-前后台系统-系统首页

在现代Web应用开发中&#xff0c;管理后台是几乎所有企业级应用不可或缺的部分。一个优秀的后台首页不仅需要提供清晰的信息展示&#xff0c;还需要具备良好的用户体验和视觉效果。本文将详细介绍如何使用Vue.js框架配合Element UI组件库和ECharts图表库&#xff0c;构建一个功…

第6节 torch.nn介绍

6.1 torch.nn.Module介绍 torch.nn.Module是 PyTorch 中构建神经网络的基础类&#xff0c;所有的神经网络模块都应该继承这个类。它提供了一种便捷的方式来组织和管理网络中的各个组件&#xff0c;包括层、参数等&#xff0c;同时还内置了许多用于模型训练和推理的功能。 官网…

python自学笔记7 可视化初步

图像的组成工具库 Matplotlib&#xff1a;绘制静态图 Plotly: 可以绘制交互式图片 图像的绘制&#xff08;Matplotlib&#xff09; 创建图形&#xff0c;轴对象 创造等差数列 # 包含后端点 arr np.linspace(0, 1, num11) # 不包含后端点 arr_no_endpoint np.linspace(0, 1, n…

GIS 常用的矢量与栅格分析工具

矢量处理工具作用典型应用缓冲区分析Buffer环境影响区域&#xff0c;空间邻近度分析等&#xff0c;例如道路周围一公里内的学校&#xff0c;噪音污染影响的范围裁剪Clip例如使用A市图层裁剪全国道路数据&#xff0c;获取A市道路数据交集Intersect识别与LUCC、分区洪水区、基础设…

http与https协议区别;vue3本地连接https地址接口报500

文章目录问题解决方案一、问题原因分析二、解决方案详解1. 保持当前配置&#xff08;推荐临时方案&#xff09;2. 更安全的方案&#xff08;推荐&#xff09;3. 环境区分配置&#xff08;最佳实践&#xff09;三、为什么开发环境不用配置&#xff1f;问题 问题&#xff1a;本地…

C语言——深入理解指针(三)

C语言——深入理解指针&#xff08;三&#xff09; 1.回调函数是什么&#xff1f; 首先我们来回顾一下函数的直接调用&#xff1a;而回调函数就是通过函数指针调用的函数。我们将函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调…

kettle 8.2 ETL项目【四、加载数据】

一、dim_store表结构,数据来源于业务表,且随时间会有增加,属于缓慢变化维(SCD)类型二 转换步骤如下 详细步骤如下

【测试报告】SoundWave(Java+Selenium+Jmeter自动化测试)

一、项目背景 随着数字音乐内容的爆炸式增长&#xff0c;用户对于便捷、高效的音乐管理与播放需求日益增强。传统的本地音乐管理方式已无法满足多设备同步、在线分享与个性化推荐等现代需求。为此&#xff0c;我们设计并开发了一款基于Spring Boot框架的SoundWave&#xff0c;旨…

C++ 类和对象详解(1)

类和对象是 C 面向对象编程的核心概念&#xff0c;它们为代码提供了更好的封装性、可读性和可维护性。本文将从类的定义开始&#xff0c;逐步讲解访问限定符、类域、实例化、对象大小计算、this 指针等关键知识&#xff0c;并对比 C 语言与 C 在实现数据结构时的差异&#xff0…

奈飞工厂:算法优化实战

推荐系统的算法逻辑与优化技巧在流媒体行业的 “用户注意力争夺战” 中&#xff0c;推荐系统是决定成败的核心武器。对于拥有2.3 亿全球付费用户的奈飞&#xff08;Netflix&#xff09;而言&#xff0c;其推荐系统每天处理数十亿次用户交互&#xff0c;最终实现了一个惊人数据&…

【人工智能99问】BERT的训练过程和推理过程是怎么样的?(24/99)

文章目录BERT的训练过程与推理过程一、预训练过程&#xff1a;学习通用语言表示1. 数据准备2. MLM任务训练&#xff08;核心&#xff09;3. NSP任务训练4. 预训练优化二、微调过程&#xff1a;适配下游任务1. 任务定义与数据2. 输入处理3. 模型结构调整4. 微调训练三、推理过程…

[TryHackMe]Challenges---Game Zone游戏区

这个房间将涵盖 SQLi&#xff08;手动利用此漏洞和通过 SQLMap&#xff09;&#xff0c;破解用户的哈希密码&#xff0c;使用 SSH 隧道揭示隐藏服务&#xff0c;以及使用 metasploit payload 获取 root 权限。 1.通过SQL注入获得访问权限 手工注入 输入用户名 尝试使用SQL注入…

北京JAVA基础面试30天打卡09

1.MySQL存储引擎及区别特性MyISAMMemoryInnoDBB 树索引✅ Yes✅ Yes✅ Yes备份 / 按时间点恢复✅ Yes✅ Yes✅ Yes集群数据库支持❌ No❌ No❌ No聚簇索引❌ No❌ No✅ Yes压缩数据✅ Yes❌ No✅ Yes数据缓存❌ NoN/A✅ Yes加密数据✅ Yes✅ Yes✅ Yes外键支持❌ No❌ No✅ Yes…

AI时代的SD-WAN异地组网如何落地?

在全球化运营与数字化转型浪潮下&#xff0c;企业分支机构、数据中心与云服务的跨地域互联需求激增。传统专线因成本高昂、部署缓慢、灵活性差等问题日益凸显不足。SD-WAN以其智能化调度、显著降本、敏捷部署和云网融合的核心优势&#xff0c;成为实现高效、可靠、安全异地组网…

css中的color-mix()函数

color-mix() 是 CSS 颜色模块&#xff08;CSS Color Module Level 5&#xff09;中引入的一个强大的颜色混合函数&#xff0c;用于在指定的颜色空间中混合两种或多种颜色&#xff0c;生成新的颜色值。它解决了传统颜色混合&#xff08;如通过透明度叠加&#xff09;在视觉一致性…

Github desktop介绍(GitHub官方推出的一款图形化桌面工具,旨在简化Git和GitHub的使用流程)

文章目录**1. 简化 Git 操作****2. 代码版本控制****3. 团队协作****4. 代码托管与共享****5. 集成与扩展****6. 跨平台支持****7. 适合的使用场景****总结**GitHub Desktop 是 GitHub 官方推出的一款图形化桌面工具&#xff0c;旨在简化 Git 和 GitHub 的使用流程&#xff0c;…

整数规划-分支定界

内容来自:b站数学建模老哥 如:3.4,先找小于3的,再找大于4的 逐个