You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:06:03