利用阿里云企业邮箱服务实现Python群发邮件

news2025/4/15 20:20:50

目录

一、阿里云企业邮箱群发邮件全流程实现

1. 准备工作与环境配置

2. 收件人列表管理

3. 邮件内容构建

4. 附件添加实现

5. 邮件发送核心逻辑

二、开发过程中遇到的问题与解决方案

1. 附件发送失败问题

2. 中文文件名乱码问题

3. 企业邮箱认证失败

三、完整工作流程总结

四、注意事项

五、最终代码展示


在现代办公场景中,批量处理邮件已成为许多企业的常规需求。Python凭借其丰富的库生态和简洁的语法,成为实现办公自动化的首选工具。日常办公中,很多企业都是通过内部邮箱进行通知,因此Python批量邮件群发功能十分重要。本文将详细介绍如何利用Python结合阿里云企业邮箱服务,实现带附件的批量邮件发送功能,并分享在实际开发过程中遇到的问题及解决方案。

一、阿里云企业邮箱群发邮件全流程实现

1. 准备工作与环境配置

首先需要确保已开通阿里云企业邮箱服务并获取正确的SMTP配置信息:

# 阿里云企业邮箱配置
SMTP_HOST = "smtp.qiye.aliyun.com"
SMTP_PORT = 465  # SSL加密端口
USERNAME = "yourname@yourcompany.com"
PASSWORD = "yourpassword"  # 注意保护敏感信息
DISPLAY_NAME = "公司邮件系统"  # 发件人显示名称

2. 收件人列表管理

使用pandas库从Excel表格中智能识别邮箱列,自动处理数据格式,此处我们默认把所有邮箱放到email.xlsx这个Excel文件中,表头用"email"或"邮箱"或"mail":

def get_recipients(excel_path):
    """从Excel获取收件人列表(自动识别邮箱列)"""
    try:
        df = pd.read_excel(excel_path, engine='openpyxl')
        # 智能识别邮箱列(支持中英文列名)
        email_col = next(
            (col for col in df.columns
             if any(kw in col.lower() for kw in ['mail', '邮箱', 'email'])),
            None
        )
        
        # 数据清洗处理
        recipients = (
            df[email_col]
                .astype(str)
                .str.strip()
                .str.lower()
                .dropna()
                .unique()
                .tolist()
        )
        return recipients
    except Exception as e:
        print(f"❌ 读取收件人列表失败: {e}")
        return []

3. 邮件内容构建

创建多部分邮件对象,支持纯文本内容和附件:

def build_email_message(receiver):
    """构建带附件的邮件消息体"""
    msg = MIMEMultipart('alternative')
    
    # 设置邮件头(符合RFC标准)
    msg['From'] = formataddr([DISPLAY_NAME, USERNAME])
    msg['Reply-to'] = USERNAME
    msg['TO'] = receiver
    msg['Message-id'] = email.utils.make_msgid()
    msg['Date'] = email.utils.formatdate()
    msg['Subject'] = Header(EMAIL_SUBJECT_TEXT, 'utf-8')
    
    # 添加纯文本正文
    textplain = MIMEText(EMAIL_CONTENT_TEXT, _subtype='plain', _charset='UTF-8')
    msg.attach(textplain)
    
    return msg

4. 附件添加实现

关键点在于正确设置附件的Content-Type和Content-Disposition头信息:

def add_attachments(msg, attachment_paths):
    """添加附件到邮件消息体"""
    for attachment_path in attachment_paths:
        try:
            with open(attachment_path, "rb") as f:
                filename = os.path.basename(attachment_path)
                att = MIMEText(f.read(), 'base64', 'utf-8')
                att["Content-Type"] = 'application/octet-stream'
                att.add_header(
                    "Content-Disposition", 
                    "attachment", 
                    filename=("gbk", "", filename)  # 解决中文文件名编码问题
                )
                msg.attach(att)
        except Exception as e:
            print(f"✗ 添加附件失败: {attachment_path} | 错误: {str(e)}")

5. 邮件发送核心逻辑

支持SSL和非SSL两种连接方式,增强兼容性:

def send_email(receiver, msg):
    """通过阿里云企业邮箱发送邮件"""
    try:
        # 尝试SSL连接
        try:
            client = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT)
        except:
            # 回退到普通连接
            client = smtplib.SMTP(SMTP_HOST, 25, timeout=5)
        
        client.login(USERNAME, PASSWORD)
        client.sendmail(USERNAME, [receiver], msg.as_string())
        client.quit()
        return True
    except Exception as e:
        print(f"✗ 发送错误: {receiver} | 错误: {str(e)}")
        return False

二、开发过程中遇到的问题与解决方案

1. 附件发送失败问题

最初使用DeepSeek生成的代码发送附件时,虽然邮件能正常发出,但收件方无法看到附件。经过排查发现:

问题原因:附件内容类型(Content-Type)设置不正确,且未正确处理文件名编码。

解决方案:参照阿里云官方Demo,明确设置:

att["Content-Type"] = 'application/octet-stream'
att.add_header("Content-Disposition", "attachment", filename=("gbk", "", filename))

而且文件不是以二进制方式打开,而是以"base64"方式读取。所以修改上传附件功能核心代码:

with open(attachment_path, "rb") as f:
    filename = os.path.basename(attachment_path)
    att = MIMEText(f.read(), 'base64', 'utf-8')
    att["Content-Type"] = 'application/octet-stream'
    att.add_header("Content-Disposition", "attachment", filename=filename)
    msg.attach(att)

2. 中文文件名乱码问题

问题表现:附件名中的中文显示为乱码

解决方案:使用MIME编码处理文件名:

from email.header import Header
filename = Header(os.path.basename(attachment_path), 'utf-8').encode()

3. 企业邮箱认证失败

问题表现:频繁出现认证失败

解决方案:

确认使用的是SMTP专用密码(非邮箱登录密码)

尝试切换SSL和非SSL端口

检查阿里云控制台是否开启了SMTP服务

三、完整工作流程总结

  • 准备阶段:配置SMTP参数,准备邮件内容和附件
  • 数据读取:从Excel智能识别并提取收件人邮箱,并存为列表
  • 邮件构建:创建MIMEMultipart对象;添加文本内容;添加附件(处理编码问题,注意阅读方式)
  • 发送测试:先向自己发送测试邮件
  • 批量发送:遍历收件人列表发送邮件
  • 结果统计:记录成功/失败数量

四、注意事项

发送频率控制:

阿里云企业邮箱有发送频率限制,建议在循环中添加延时:

time.sleep(5)  # 5秒间隔

错误重试机制:对发送失败的邮件实现自动重试:

max_retries = 2
for attempt in range(max_retries):
    if send_email_with_attachment(to_email):
        break

日志记录:建议将发送结果记录到日志文件,便于后续跟踪:

with open("send_log.txt", "a") as log:
    log.write(f"{time.ctime()},{to_email},{'成功' if result else '失败'}\n")

通过本文介绍的方法,企业可以高效地利用Python和阿里云企业邮箱服务实现批量邮件发送,大幅提升办公效率。

在群发邮件时,一定要正确处理邮件格式、附件编码以及错误处理机制,这些细节决定了程序的稳定性和可靠性。

五、最终代码展示

使用本代码时,所有邮件地址放到email.xlsx里,新建附件文件夹,所有附件都放在其中。

代码如下:

# -*- coding:utf-8 -*-
import smtplib
import email.utils  # 修改这里,直接导入email.utils
import pandas as pd
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr
from email.header import Header
import email.utils
import traceback, os, time
from glob import glob

# 阿里云企业邮箱配置
SMTP_HOST = "smtp.qiye.aliyun.com"
SMTP_PORT = 465
USERNAME = "Your EMAIL"
PASSWORD = "YOUR PASSWORD"
DISPLAY_NAME = "公司邮件系统"

# 邮件内容配置(重命名变量以避免冲突)
EMAIL_SUBJECT_TEXT = "重要通知:系统升级维护"
EMAIL_CONTENT_TEXT = """
尊敬的同事:

根据公司IT规划,我们将于2023年12月15日(周五)20:00至24:00进行系统升级维护。
升级期间所有业务系统将暂停访问,请提前做好工作安排。

如有疑问请联系IT支持部门:
电话:400-123-4567
邮箱:support@yourcompany.com

IT管理中心
"""

# 修改附件路径为绝对路径(示例)
ATTACHMENTS = [os.path.abspath(file) for file in glob(os.path.join("附件", "*")) 
              if os.path.isfile(file)]

def get_recipients(excel_path):
    """从Excel获取收件人列表(自动识别邮箱列)"""
    try:
        if not os.path.isfile(excel_path):
            raise FileNotFoundError(f"文件不存在: {excel_path}")

        df = pd.read_excel(excel_path, engine='openpyxl')
        email_col = next(
            (col for col in df.columns
             if any(kw in col.lower() for kw in ['mail', '邮箱', 'email'])),
            None
        )

        if not email_col:
            available_columns = ", ".join(df.columns)
            raise ValueError(f"未找到邮箱列,可用列有: {available_columns}")

        recipients = (
            df[email_col]
                .astype(str)
                .str.strip()
                .str.lower()
                .dropna()
                .unique()
                .tolist()
        )

        print(f"从Excel读取到 {len(recipients)} 个有效邮箱地址")
        return recipients

    except Exception as e:
        print(f"❌ 读取收件人列表失败: {e}")
        return []

def send_email_with_attachment(receiver):
    """发送带附件的邮件(阿里云企业邮箱专用)"""
    try:
        # 创建多部分邮件对象
        msg = MIMEMultipart('alternative')
        
        # 设置邮件头(符合阿里云demo规范)
        msg['From'] = formataddr([DISPLAY_NAME, USERNAME])
        msg['Reply-to'] = USERNAME
        msg['TO'] = receiver
        msg['Message-id'] = email.utils.make_msgid()  # 现在可以正确调用了
        msg['Date'] = email.utils.formatdate()  # 现在可以正确调用了
        msg['Subject'] = EMAIL_SUBJECT_TEXT

        # 添加邮件正文
        textplain = MIMEText(EMAIL_CONTENT_TEXT, _subtype='plain', _charset='UTF-8')
        msg.attach(textplain)
        
        # 添加附件(按照阿里云demo方式)
        for attachment_path in ATTACHMENTS:
            try:
                if not os.path.isfile(attachment_path):
                    print(f"⚠ 附件不存在: {attachment_path}")
                    continue
                
                with open(attachment_path, "rb") as f:
                    filename = os.path.basename(attachment_path)
                    att = MIMEText(f.read(), 'base64', 'utf-8')
                    att["Content-Type"] = 'application/octet-stream'
                    att.add_header("Content-Disposition", "attachment", filename=filename)
                    msg.attach(att)
                    print(f"✓ 已添加附件: {filename}")
                    
            except Exception as e:
                print(f"✗ 添加附件失败: {attachment_path} | 错误: {str(e)}")
                traceback.print_exc()

        # 建立连接并发送(按照阿里云demo方式)
        try:
            client = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT)
            print('smtp_ssl----连接服务器成功,现在开始检查账号密码')
        except Exception as e1:
            client = smtplib.SMTP(SMTP_HOST, 25, timeout=5)
            print('smtp----连接服务器成功,现在开始检查账号密码')
        except Exception as e2:
            print('抱歉,连接服务超时')
            return False
            
        try:
            client.login(USERNAME, PASSWORD)
            print('账密验证成功')
        except:
            print('抱歉,账密验证失败')
            return False

        client.sendmail(USERNAME, [receiver], msg.as_string())
        client.quit()
        print(f"✓ 已发送至: {receiver}")
        return True

    except Exception as e:
        print(f"✗ 发送错误: {receiver} | 错误: {str(e)}")
        traceback.print_exc()
        return False

if __name__ == "__main__":
    print("\n=== 阿里云企业邮箱批量发送系统 ===")
    print(f"发件人: {DISPLAY_NAME} <{USERNAME}>")

    # 步骤1:获取收件人
    recipients = get_recipients("email.xlsx")

    if not recipients:
        print("❌ 程序终止:没有有效的收件人地址")
    else:
        # 步骤2:测试发送(给自己发一封)
        print("\n正在发送测试邮件...")
        if not send_email_with_attachment(USERNAME):
            print("❌ 测试邮件发送失败,请检查配置")
        else:
            # 步骤3:批量发送
            print("\n开始批量发送...")
            success_count = 0
            for to_email in recipients:
                if to_email != USERNAME.lower():  # 避免重复发送
                    if send_email_with_attachment(to_email):
                        success_count += 1

            # 发送结果统计
            print(f"\n发送完成: 成功 {success_count}/{len(recipients)}")
            if success_count < len(recipients):
                print("⚠ 注意:部分邮件发送失败,请查看上方日志")

    print("\n=== 程序执行结束 ===")

代码运行展示:

参考内容:
如何使用Python3.6及以上版本投递SMTP邮件_阿里邮箱(Alibaba Mail)-阿里云帮助中心

https://zhuanlan.zhihu.com/p/1894738611020214432

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

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

相关文章

08-JVM 面试题-mk

文章目录 1.JVM 的各部分组成2.运行时数据区2.1.什么是程序计数器?2.2.你能给我详细的介绍Java堆吗?2.3.能不能解释一下方法区?2.3.1常量池2.3.2.运行时常量池2.4.什么是虚拟机栈?2.4.1.垃圾回收是否涉及栈内存?2.4.2.栈内存分配越大越好吗?2.4.3.方法内的局部变量是否线…

PostgreSQL技术大讲堂 - 第86讲:数据安全之--data_checksums天使与魔鬼

PostgreSQL技术大讲堂 - 第86讲&#xff0c;主题&#xff1a;数据安全之--data_checksums天使与魔鬼 1、data_checksums特性 2、避开DML规则&#xff0c;嫁接非法数据并合法化 3、避开约束规则&#xff0c;嫁接非法数据到表中 4、避开数据检查&#xff0c;读取坏块中的数据…

从宇树摇操avp_teleoperate到unitree_IL_lerobot:如何基于宇树人形进行二次开发(含Open-TeleVision源码解析)

前言 如之前的文章所述&#xff0c;我司「七月在线」正在并行开发多个订单&#xff0c;目前正在全力做好每一个订单&#xff0c;因为保密协议的原因&#xff0c;暂时没法拿出太多细节出来分享 ​但可以持续解读我们所创新改造或二次开发的对象&#xff0c;即解读paper和开源库…

告别 ifconfig:为什么现代 Linux 系统推荐使用 ip 命令

告别 ifconfig&#xff1a;为什么现代 Linux 系统推荐使用 ip 命令 ifconfig 指令已经被视为过时的工具&#xff0c;不再是查看和配置网络接口的推荐方式。 与 netstat 被 ss 替代类似。 本文简要介绍 ip addr 命令的使用 简介ip ifconfig 属于 net-tools 包&#xff0c;这个…

MySQL——MVCC(多版本并发控制)

目录 1.MVCC多版本并发控制的一些基本概念 MVCC实现原理 记录中的隐藏字段 undo log undo log 版本链 ReadView 数据访问规则 具体实现逻辑 总结 1.MVCC多版本并发控制的一些基本概念 当前读&#xff1a;该取的是记录的最新版本&#xff0c;读取时还要保证其他并发事务…

Gateway-网关-分布式服务部署

前言 什么是API⽹关 API⽹关(简称⽹关)也是⼀个服务, 通常是后端服务的唯⼀⼊⼝. 它的定义类似设计模式中的Facade模式(⻔⾯模式, 也称外观模式). 它就类似整个微服务架构的⻔⾯, 所有的外部客⼾端访问, 都需要经过它来进⾏调度和过滤. 常⻅⽹关实现 Spring Cloud Gateway&a…

Docker部署MySQL大小写不敏感配置与数据迁移实战20250409

Docker部署MySQL大小写不敏感配置与数据迁移实战 &#x1f9ed; 引言 在企业实际应用中&#xff0c;尤其是使用Java、Hibernate等框架开发的系统&#xff0c;MySQL默认的大小写敏感特性容易引发各种兼容性问题。特别是在Linux系统中部署Docker版MySQL时&#xff0c;默认行为可…

面试题之网络相关

最近开始面试了&#xff0c;410面试了一家公司 问了我几个网络相关的问题&#xff0c;我都不会&#xff01;&#xff01;现在来恶补一下&#xff0c;整理到博客中&#xff0c;好难记啊&#xff0c;虽然整理下来了。在这里先祝愿大家在现有公司好好沉淀&#xff0c;定位好自己的…

[春秋云镜] Tsclient仿真场景

文章目录 靶标介绍&#xff1a;外网mssql弱口令SweetPotato提权上线CSCS注入在线用户进程上线 内网chisel搭建代理密码喷洒攻击映像劫持 -- 放大镜提权krbrelayup提权Dcsync 参考文章 考点: mssql弱口令SweetPotato提权CS注入在线用户进程上线共享文件CS不出网转发上线密码喷洒…

数据集 handpose_x_plus 3D RGB 三维手势 - 手工绘画 场景 draw picture

数据集 handpose 相关项目地址&#xff1a;https://github.com/XIAN-HHappy/handpose_x_plus 样例数据下载地址&#xff1a;数据集handpose-x-plus3DRGB三维手势-手工绘画场景drawpicture资源-CSDN文库

deskflow使用教程:一个可以让两台电脑鼠标键盘截图剪贴板共同使用的开源项目

首先去开源网站下载&#xff1a;Release v1.21.2 deskflow/deskflow 两台电脑都要下载这个文件 下载好后直接打开找到你想要的exe desflow.exe 然后你打开他&#xff0c;将两台电脑的TLS都关掉 下面步骤两台电脑都要完成&#xff1a; 电脑点开edit-》preferences 把这个取…

详解MYSQL表空间

目录 表空间文件 表空间文件结构 行格式 Compact 行格式 变长字段列表 NULL值列表 记录头信息 列数据 溢出页 数据页 当我们使用MYSQL存储数据时&#xff0c;数据是如何被组织起来的&#xff1f;索引又是如何组织的&#xff1f;在本文我们将会解答这些问题。 表空间文…

[Windows] 音速启动 1.0.0.0

[Windows] 音速启动 链接&#xff1a;https://pan.xunlei.com/s/VONiGZhtsxpPzze0lDIH-mR9A1?pwdxu7f# [Windows] 音速启动 1.0.0.0 音速启动是一款桌面管理软件&#xff0c;以仿真QQ界面的形式结合桌面工具的特点&#xff0c;应用于软件文件夹网址的快捷操作。

Hyper-V 虚拟机配置静态IP并且映射到局域网使用

环境 win11hyper-v麒麟v10 配置 编辑文件 vi /etc/sysconfig/network-scripts/ifcfg-eth0文件内容 GATEWAY 需要参考网络中配置的网关地址 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITyes IPV6_AUTOCONFyes …

操作系统基础:06 操作系统历史

我们前面已经讲过了操作系统的基本轮廓、启动过程以及系统调用等相关内容&#xff0c;就如同揭开了钢琴的盖子&#xff0c;对操作系统有了初步的表面认识。从现在起&#xff0c;我们要更深入地剖析操作系统&#xff0c;就像分解钢琴一样&#xff0c;探究其各个部分的构成、原理…

【大模型微调】如何解决llamaFactory微调效果与vllm部署效果不一致如何解决

以下个人没整理太全 一、生成式语言模型的对话模板介绍 使用Qwen/Qwen1.5-0.5B-Chat训练 对话模板不一样。回答的内容就会不一样。 我们可以看到例如qwen模型的tokenizer_config.json文件&#xff0c;就可以看到对话模板&#xff0c;一般同系列的模型&#xff0c;模板基本都…

【2025最新】windows本地部署LightRAG,完成neo4j知识图谱保存

之前在服务器部署neo4j失败&#xff0c;无奈只能在本地部署&#xff0c;导致后期所有使用的知识图谱数据都存在本地&#xff0c;这里为了节省时间&#xff0c;先在本地安装LigthRAG完成整个实验流程&#xff0c;后续在学习各种服务器部署和端口调用。从基础和简单的部分先做起来…

14、nRF52xx蓝牙学习(串口 UART 和 UARTE 外设应用)

一、UART 功能描述 串口 UART 也称为通用异步收发器。是各种处理器中常用了通信接口&#xff0c;在 nRF52 芯片中&#xff0c; UART 具有以下特点&#xff1a; ● 全双工操作 ● 自动流控 ● 奇偶校验产生第 9 位数据 串口 UART 的数据发送与接收流程 : ◆硬件配置…

DeepSeek轻松入门教程——从入门到精通

大家好&#xff0c;我是吾鳴。 今天吾鳴要给大家分享一份DeepSeek小白轻松入门指导手册——《DeepSeek 15天指导手册&#xff0c;从入门到精通》。指导手册分为基础入门对话篇、效率飞跃篇、场景实战篇、高手进化篇等&#xff0c;按照指导手册操作&#xff0c;DeepSeek从入门到…

Vue2 老项目升级 Vue3 深度解析教程

Vue2 老项目升级 Vue3 深度解析教程 摘要 Vue3 带来了诸多改进和新特性&#xff0c;如性能提升、组合式 API、更好的 TypeScript 支持等&#xff0c;将 Vue2 老项目升级到 Vue3 可以让项目获得这些优势。本文将深入解析升级过程&#xff0c;涵盖升级前的准备工作、具体升级步骤…