如何使用Python自动发送带PDF附件的个性化邮件?
Python批量发送带PDF附件的个性化邮件解决方案
以下是修改后的完整可运行代码,保留了原有逻辑的同时新增了PDF附件支持,按最佳实践做了函数封装,每行代码都添加了注释方便理解:
import os import smtplib, ssl import csv # 导入邮件构造所需的内置模块,支持多内容类型(正文+附件) from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders def read_contacts(csv_path): """读取csv联系人列表,返回(姓名, 邮箱)元组组成的列表""" contacts = [] with open(csv_path, 'r', encoding='utf-8') as f: reader = csv.reader(f) next(reader) # 跳过表头行 for row in reader: if len(row) >= 2: # 过滤空行/无效行 contacts.append((row[0].strip(), row[1].strip())) return contacts def build_email(to_name, to_email, from_email, subject, body_template, attachment_path): """构建带PDF附件的个性化邮件对象""" # 初始化多部分邮件对象,支持同时存储正文和附件 msg = MIMEMultipart() msg['From'] = from_email msg['To'] = to_email msg['Subject'] = subject # 渲染个性化正文,添加到邮件对象 body = body_template.format(name=to_name) msg.attach(MIMEText(body, 'plain', 'utf-8')) # 读取PDF文件,处理为邮件附件格式 with open(attachment_path, 'rb') as f: # 创建附件基础对象 part = MIMEBase('application', 'octet-stream') part.set_payload(f.read()) # 把附件内容编码为base64格式,适配邮件传输协议要求 encoders.encode_base64(part) # 设置附件头信息,指定显示的文件名 part.add_header( 'Content-Disposition', f'attachment; filename="{os.path.basename(attachment_path)}"' ) # 把附件添加到邮件对象中 msg.attach(part) # 转换为字符串格式供发送接口调用 return msg.as_string() def send_bulk_emails(contacts_csv, attachment_path, email_subject, email_body_template, smtp_server="smtp.gmail.com", smtp_port=465): """批量发送邮件的主函数,统一管控整个发送流程""" # 从环境变量读取发件人凭证,避免硬编码泄露敏感信息 sender_email = os.environ.get("email_user") sender_pass = os.environ.get("email_app_pass") if not sender_email or not sender_pass: raise ValueError("请先配置系统环境变量email_user和email_app_pass") # 读取联系人列表 contacts = read_contacts(contacts_csv) if not contacts: print("未读取到有效联系人信息") return # 创建SSL安全上下文,保障传输安全 context = ssl.create_default_context() # 用上下文管理器连接SMTP服务器,无需手动关闭连接 with smtplib.SMTP_SSL(smtp_server, smtp_port, context=context) as server: server.login(sender_email, sender_pass) print("邮箱登录成功,开始发送邮件") # 遍历联系人逐个发送个性化邮件 for name, email in contacts: try: email_msg = build_email(name, email, sender_email, email_subject, email_body_template, attachment_path) server.sendmail(sender_email, email, email_msg) print(f"发送成功:{name} <{email}>") except Exception as e: print(f"发送失败:{name} <{email}>,错误信息:{str(e)}") print("所有邮件任务处理完成") if __name__ == "__main__": # 自定义邮件配置 SUBJECT = "Testing Python Automation" BODY_TEMPLATE = """Hi {name},\n\nThis email was entirely generated in Python. So cool! """ # 调用主函数执行发送 send_bulk_emails( contacts_csv="contacts_test.csv", attachment_path="test.pdf", # 替换为你要发送的PDF文件实际路径 email_subject=SUBJECT, email_body_template=BODY_TEMPLATE )
核心优化说明
- 新增邮件构造模块:放弃原有的简单字符串拼接邮件方式,使用Python内置
email系列模块支持多附件格式的邮件构造 - 按单一职责封装函数:将读取联系人、构造邮件、发送流程拆分为三个独立函数,可读性、可复用性更高
- 修复原代码冗余逻辑:移除了
with上下文管理器内多余的close()、quit()调用,上下文会自动处理资源释放 - 新增异常捕获:单条邮件发送失败不会中断整个批量发送流程,会打印错误信息方便排查
使用注意事项
- 提前配置好系统环境变量
email_user(发件人邮箱)和email_app_pass(Gmail需使用应用专用密码,其他邮箱替换对应SMTP地址和端口即可) contacts_test.csv保持表头为name,email,每行对应一个收件人信息- 代码中
test.pdf替换为你要发送的PDF文件实际路径即可
内容的提问来源于stack exchange,提问作者stepad
相关产品推荐
相关产品推荐

