当前位置: 首页 > news >正文

python办自动化--读取邮箱中特定的邮件,并下载特定的附件

系列文章目录

python办公自动化–数据可视化(pandas+matplotlib)–生成条形图和饼状图
python办公自动化–数据可视化(pandas+matplotlib)–生成折线图
python办公自动化–数据可视化(pandas读取excel文件,matplotlib生成可视化图表)
python办公自动化-openpyxl学习-工资表生成工资条
python办公自动化–使用将csv大文件分割为xlsx小文件
python办公自动化----使用pandas和os合并多个订单表
三种方法批量填充订单表中的空白单元格–python,excel vba,excel
python办自动化–批量发送带附件的变量邮件(示例:给员工发送工资条)

文章目录

  • 系列文章目录
  • 前言
  • 一、登录邮箱
  • 二、记录日志
  • 三、搜索解析邮件
  • 四、设置搜索条件
    • 1.前五十封
    • 2.发件人白名单
    • 3.邮件主题白名单
    • 4.发件日期为今天
  • 五、下载.xlsx附件
  • 六、关闭邮箱安全退出
  • 七.代码整合(可直接使用)
  • 总结


前言

今天我们来学习使用python读取邮箱中的邮件,并且将里面的附件表格下载到我们本地的文件夹中。

我们这个系列叫做python自动化办公,要实现真正意义上的办公自动化,就是要解放我们的双手,实现全过程的自动化。基本上数据分析的工作分为以下流程:收集数据----清晰整理数据-----分析数据-----发送结果
我们前面学习了使用pandas清洗整理数据(结合我的另一个专栏–python数据分析),学习了python发送带附件的变量邮件(发送结果),至于最精彩最关键的分析数据这个阶段,我们也略微接触了一些,今天我们就好好学习一下python读取邮件。

情景设置:你是公司的数据分析师,每天各个部门的负责人每天八点会将昨天的数据作为附件表格,以“order”和“kucun”作为主题发送到你的邮箱,你需要使用将每天的邮件里面的附件下载到本地的E盘的“附件下载”这个文件夹中。

一、登录邮箱

# 导入imaplib库用于IMAP协议邮箱操作
import imaplib
# 邮箱登录用户名
email_user = "youremail"
# 邮箱授权码(非登录密码),用于IMAP协议认证
email_password = "yourpassword"
# IMAP服务器地址,Foxmail使用QQ邮箱的IMAP服务器
imap_server = "imap.qq.com"
mail = imaplib.IMAP4_SSL(imap_server)
# 使用用户名和授权码登录邮箱
mail.login(email_user, email_password)
print("邮箱登录成功")

首先我们要在邮箱的设置界面打开我们的smtp/imap/pop3等服务,在这个过程中,我们会获得一个授权码,通过这个授权码,我们就可以将连接到我们的邮箱。我这里登录的是QQ邮箱,所以服务器地址就是“imap.qq.com”,那么大家在尝试登录的时候,就要先把上面的用户名,授权码以及邮箱地址改为自己的地址和授权码。
登录成功以后,我们才可以进行下面的操作。

在这里插入图片描述

二、记录日志

因为我们读取邮件下载附件这个动作并不是只做一次,所以我们需要设置日志来记录脚本的运行信息。

# 导入logging库用于记录日志
import logging
# 导入os库用于文件和目录操作
import os  
# 附件下载目录,使用双反斜杠转义
download_folder = "E:\\附件下载"# 日志系统配置
# ============
logging.basicConfig(level=logging.DEBUG,  # 设置日志级别为DEBUG,记录所有级别日志format="%(asctime)s - %(levelname)s - %(message)s",  # 日志格式: 时间-级别-消息handlers=[# 文件处理器,将日志写入到下载目录中的email_downloader.log文件logging.FileHandler(os.path.join(download_folder, "email_downloader.log")),# 控制台处理器,将日志输出到标准输出logging.StreamHandler()]
)
# 获取当前模块的日志记录器实例
logger = logging.getLogger(__name__)

这里我们在E盘新建一个文件夹叫做“附件下载”,在这里面我们会新建一个log文件来保存日志

三、搜索解析邮件

# 导入email.utils用于邮件日期解析
import email.utils
# 导入email.header用于解码邮件头
import email.header
# 选择收件箱文件夹
mail.select("inbox")
# 执行IMAP搜索命令
status, messages = mail.search(None, search_criteria)
if status != "OK":  # 检查搜索是否成功raise Exception("邮件搜索失败")
# 解析邮件内容为Message对象
email_message = email.message_from_bytes(msg_data[0][1])

四、设置搜索条件

# 1. FROM 白名单中的任一发件人(使用OR连接)
# 2. SINCE 今天日期
# 3. HAS attachment 必须包含附件
from_clause = " OR ".join([f'FROM "{sender}"' for sender in sender_whitelist])
search_criteria = f'({from_clause} SINCE "{today}" HAS attachment)'
logger.info(f"严格模式: 只处理今天({today})收到的邮件")
logger.info(f"发件人白名单: {sender_whitelist}")
logger.info(f"主题白名单: {subject_whitelist}")
logger.info(f"搜索条件: {search_criteria}")

1.前五十封

代码如下(示例):


# 获取邮件ID列表并只取最后50个(最新的50封)
mail_ids = messages[0].split()[-50:]
logger.info(f"找到 {len(mail_ids)} 封匹配邮件,只处理最近50封")# 从最新到最旧处理邮件
for mail_id in reversed(mail_ids):# 获取邮件完整内容(RFC822格式)status, msg_data = mail.fetch(mail_id, "(RFC822)")if status != "OK":  # 如果获取失败则跳过continue

通常我们的邮箱每天不会收到太多邮件,50封这个量对于一天来说应该绰绰有余了

2.发件人白名单

# 发件人邮箱白名单列表
sender_whitelist = ["fajianren1@163.com",  # 示例邮箱1"fajianren2@gmail.com",  # 示例邮箱2
]
if from_header not in sender_whitelist:  # 严格检查白名单logger.debug(f"跳过非白名单发件人: {from_header}")continue

通常每天给我们发送邮件的人是固定的,我们只需要下载他们发送的邮件里面的附件,因此添加发件人白名单这一个限制条件。

3.邮件主题白名单

# 主题白名单列表(精确匹配)
subject_whitelist = ["order",  # 示例主题1"kucun"
]
subject = email_message['Subject']
if subject not in subject_whitelist:  # 严格检查主题白名单logger.debug(f"跳过非白名单主题: {subject}")continue

有时候这部门负责人给我们发的邮件并不是昨天的销售数据,所以我们要用邮件主题进行区分
这里和上面的发件人白名单是一样的处理逻辑,这里注意两点;1.使用英文作为邮件主题,比如上面的"order"和"kucun";2.注意大小写,这里是严格匹配,所以大小写需要保持一致

4.发件日期为今天

# 从datetime导入datetime用于日期时间处理
from datetime import datetime# 邮件日期验证
# ===========
try:# 解析邮件日期头mail_date = email.utils.parsedate_to_datetime(email_message['Date']) if email_message['Date'] else Noneif mail_date and mail_date.date() != datetime.now().date():  # 如果不是今天则跳过logger.debug(f"跳过非今天({mail_date.date()})收到的邮件")continue

这里我们需要设置时间为条件,确保我们每天下载的都是最新的数据,要不然会重复下载前面几天的数据。

五、下载.xlsx附件

# 遍历邮件各部分
# =============
for part in email_message.walk():  # 递归遍历邮件所有部分if part.get_content_maintype() == "multipart":  # 跳过multipart容器部分continue# 检查是否为.xlsx附件# ==================filename = part.get_filename()  # 获取附件文件名content_type = part.get_content_type()  # 获取内容类型# 判断条件:# 1. 文件名以.xlsx结尾,或# 2. 内容类型是Excel文件if (filename and filename.lower().endswith(".xlsx")) or \(content_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"):# 处理无文件名的情况# ================if not filename:  # 如果附件没有文件名# 使用邮件ID生成唯一文件名filename = f"attachment_{mail_id.decode()}.xlsx"# 文件名处理# =========# 解码邮件头中的文件名decoded_name = email.header.decode_header(filename)[0][0]if isinstance(decoded_name, bytes):decoded_name = decoded_name.decode('utf-8')today_str = datetime.now().strftime("%Y%m%d")  # 获取当前日期字符串base_name, ext = os.path.splitext(decoded_name)  # 拆分文件名和扩展名# 生成新文件名格式:YYYYMMDD-原文件名.xlsxnew_filename = f"{today_str}-{base_name}{ext}"# 拼接完整的文件保存路径filepath = os.path.join(download_folder, new_filename)# 附件保存处理# ===========if not os.path.exists(filepath):  # 检查文件是否已存在# 以二进制模式保存附件with open(filepath, "wb") as f:f.write(part.get_payload(decode=True))  # 解码并写入附件内容logger.info(f"下载附件: {filename} -> {new_filename}")else:  # 文件已存在则跳过logger.info(f"附件已存在,跳过: {new_filename}")

首先我们根据文件类型筛选出.xlsx格式的附件,然后我们将当天的日期和附件的名字结合在一起,作为新名字,最后我们将附件以新名字命名保存到E盘的‘附件下载”这个文件夹中


六、关闭邮箱安全退出

    # 关闭邮箱连接# ===========mail.close()  # 关闭当前邮箱文件夹mail.logout()  # 退出IMAP会话logger.info("处理完成")  # 记录处理完成信息except Exception as e:  # 异常处理部分# 记录错误信息,包含堆栈跟踪logger.error(f"发生错误: {str(e)}", exc_info=True)  # 确保邮箱连接被正确关闭if 'mail' in locals() and mail.state != 'LOGOUT':  # 检查mail对象是否存在且未退出try:mail.logout()  # 尝试安全退出except:pass  # 忽略退出时的任何错误if __name__ == "__main__":# 当脚本直接运行时执行主函数download_specific_attachments()  

七.代码整合(可直接使用)

# 导入imaplib库用于IMAP协议邮箱操作
import imaplib
# 导入email库用于解析邮件内容
import email  
# 导入os库用于文件和目录操作
import os  
# 导入logging库用于记录日志
import logging  
# 从datetime导入datetime用于日期时间处理
from datetime import datetime
# 导入email.utils用于邮件日期解析
import email.utils
# 导入email.header用于解码邮件头
import email.headerdef download_specific_attachments():"""下载指定发件人今天发送的邮件中的.xlsx附件主要功能:1. 连接IMAP邮箱服务器2. 搜索指定发件人今天发送的邮件3. 只处理最近20封邮件4. 解析邮件内容并识别.xlsx附件5. 下载附件并按指定格式重命名6. 记录完整操作日志"""# 邮箱服务器配置部分# ====================# 邮箱登录用户名email_user = "youremail"# 邮箱授权码(非登录密码),用于IMAP协议认证email_password = "yourpassword"# IMAP服务器地址,Foxmail使用QQ邮箱的IMAP服务器imap_server = "imap.qq.com"# 发件人邮箱白名单列表sender_whitelist = ["fajianren1@163.com",  # 示例邮箱1"fajianren2@gmail.com",  # 示例邮箱2]# 主题白名单列表(精确匹配)subject_whitelist = ["order" , # 示例主题1"kucun" ]# 附件下载目录,使用双反斜杠转义download_folder = "E:\\附件下载"# 日志系统配置# ============logging.basicConfig(level=logging.DEBUG,  # 设置日志级别为DEBUG,记录所有级别日志format="%(asctime)s - %(levelname)s - %(message)s",  # 日志格式: 时间-级别-消息handlers=[# 文件处理器,将日志写入到下载目录中的email_downloader.log文件logging.FileHandler(os.path.join(download_folder, "email_downloader.log")),# 控制台处理器,将日志输出到标准输出logging.StreamHandler()])# 获取当前模块的日志记录器实例logger = logging.getLogger(__name__)# 创建附件下载目录# exist_ok=True表示如果目录已存在不会报错os.makedirs(download_folder, exist_ok=True)try:# 邮箱连接和登录部分# ==================logger.info("正在连接到IMAP服务器...")# 创建IMAP4_SSL安全连接对象mail = imaplib.IMAP4_SSL(imap_server)# 使用用户名和授权码登录邮箱mail.login(email_user, email_password)# 记录登录成功状态logger.info("登录成功")# 选择收件箱文件夹mail.select("inbox")# 构造IMAP搜索条件# ===============# 获取今天的日期,格式化为IMAP要求的格式(如"21-Jul-2025")today = datetime.now().strftime("%d-%b-%Y")# 构造IMAP搜索条件:# 1. FROM 白名单中的任一发件人(使用OR连接)# 2. SINCE 今天日期# 3. HAS attachment 必须包含附件from_clause = " OR ".join([f'FROM "{sender}"' for sender in sender_whitelist])search_criteria = f'({from_clause} SINCE "{today}" HAS attachment)'logger.info(f"严格模式: 只处理今天({today})收到的邮件")logger.info(f"发件人白名单: {sender_whitelist}")logger.info(f"主题白名单: {subject_whitelist}")logger.info(f"搜索条件: {search_criteria}")# 执行IMAP搜索命令status, messages = mail.search(None, search_criteria)if status != "OK":  # 检查搜索是否成功raise Exception("邮件搜索失败")# 处理搜索结果# =============# 获取邮件ID列表并只取最后20个(最新的20封)mail_ids = messages[0].split()[-20:]logger.info(f"找到 {len(mail_ids)} 封匹配邮件,只处理最近20封")# 从最新到最旧处理邮件for mail_id in reversed(mail_ids):# 获取邮件完整内容(RFC822格式)status, msg_data = mail.fetch(mail_id, "(RFC822)")if status != "OK":  # 如果获取失败则跳过continue# 解析邮件内容为Message对象email_message = email.message_from_bytes(msg_data[0][1])# 验证发件人是否在白名单中# ========================# 验证邮件主题是否在白名单中,并处理中文汉字主题# ========================from_header = email.utils.parseaddr(email_message['From'])[1]  # 解析发件人邮箱if from_header not in sender_whitelist:  # 严格检查白名单logger.debug(f"跳过非白名单发件人: {from_header}")continuetry:# 尝试将邮件主题解码为utf-8,处理可能的中文字符问题subject = email_message['Subject'].encode('latin1').decode('utf-8')except UnicodeDecodeError:# 如果解码失败,则记录日志并跳过该邮件logger.warning("邮件主题编码问题,跳过该邮件")continueif subject not in subject_whitelist:  # 严格检查主题白名单logger.debug(f"跳过非白名单主题: {subject}")continue# 邮件日期验证# ===========try:# 解析邮件日期头mail_date = email.utils.parsedate_to_datetime(email_message['Date']) if email_message['Date'] else Noneif mail_date and mail_date.date() != datetime.now().date():  # 如果不是今天则跳过logger.debug(f"跳过非今天({mail_date.date()})收到的邮件")continueelif not mail_date:  # 如果邮件没有日期信息则默认处理logger.debug("邮件无日期信息,默认处理")except Exception as e:  # 日期解析错误处理logger.warning(f"日期解析错误: {str(e)},默认处理")# 遍历邮件各部分# =============for part in email_message.walk():  # 递归遍历邮件所有部分if part.get_content_maintype() == "multipart":  # 跳过multipart容器部分continue# 检查是否为.xlsx附件# ==================filename = part.get_filename()  # 获取附件文件名content_type = part.get_content_type()  # 获取内容类型# 判断条件:# 1. 文件名以.xlsx结尾,或# 2. 内容类型是Excel文件if (filename and filename.lower().endswith(".xlsx")) or \(content_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"):# 处理无文件名的情况# ================if not filename:  # 如果附件没有文件名# 使用邮件ID生成唯一文件名filename = f"attachment_{mail_id.decode()}.xlsx"# 文件名处理# =========# 解码邮件头中的文件名decoded_name = email.header.decode_header(filename)[0][0]if isinstance(decoded_name, bytes):decoded_name = decoded_name.decode('utf-8')today_str = datetime.now().strftime("%Y%m%d")  # 获取当前日期字符串base_name, ext = os.path.splitext(decoded_name)  # 拆分文件名和扩展名# 生成新文件名格式:YYYYMMDD-原文件名.xlsxnew_filename = f"{today_str}-{base_name}{ext}"# 拼接完整的文件保存路径filepath = os.path.join(download_folder, new_filename)  # 附件保存处理# ===========if not os.path.exists(filepath):  # 检查文件是否已存在# 以二进制模式保存附件with open(filepath, "wb") as f:  f.write(part.get_payload(decode=True))  # 解码并写入附件内容logger.info(f"下载附件: {filename} -> {new_filename}")  else:  # 文件已存在则跳过logger.info(f"附件已存在,跳过: {new_filename}")  # 关闭邮箱连接# ===========mail.close()  # 关闭当前邮箱文件夹mail.logout()  # 退出IMAP会话logger.info("处理完成")  # 记录处理完成信息except Exception as e:  # 异常处理部分# 记录错误信息,包含堆栈跟踪logger.error(f"发生错误: {str(e)}", exc_info=True)  # 确保邮箱连接被正确关闭if 'mail' in locals() and mail.state != 'LOGOUT':  # 检查mail对象是否存在且未退出try:mail.logout()  # 尝试安全退出except:pass  # 忽略退出时的任何错误if __name__ == "__main__":# 当脚本直接运行时执行主函数download_specific_attachments()  

点击运行后,在我本地的E盘里面的“附件下载”这个文件夹内容如下
在这里插入图片描述
可以看到,成功下载了附件以及生成了log日志
那么大家在使用上面的代码的时候,需要修改的就是邮箱,授权码,以及发件人白名单和邮件主题白名单了。

总结

今天我们学习了用python读取邮箱里面的邮件,并且下载附件到本地,我们还设置了时间、发件人以及邮件主题等多个限制条件。其实这里面可以玩的东西远不止这么多,我大家可以自行去探索。

OK,那么到今天为止,我们就讲完了python发邮件以及收邮件,我们得到了数据源,那么下阶段将正式开始学习python处理数据,当然主要是excel表格的数据,包括一些匹配、聚合等等,当然如果有空也会更新python处理word或者pptx等。

至于下阶段的更新,我会放在python数据分析这个专栏。如果大家对上面的文章有不懂的地方,也可以随时私信我。同样的,如果大家发现哪里不对,也欢迎批评指正哈。
大家有兴趣的可以点个关注以及免费的赞赞哟,爱你们!!

http://www.lryc.cn/news/595635.html

相关文章:

  • 在Android开发中,如何获取到手机设备的PIN码?
  • 使用python中的pymysql库,并且转化为数组元组数据
  • 重构创作边界:川翔云电脑 - UE5云端超算引擎​
  • mysql_innodb_cluster_metadata源数据库
  • 7.22总结mstp,vrrp
  • 如何给手机充电才不伤电池?
  • Selenium+Java 自动化测试入门到实践:从环境搭建到元素操作
  • STM32 GPIO(通用输入输出)详解:从模式原理到实战应用
  • 如何把jar包打成docker镜像(SpringBoot项目打包成Docker )部署到Linux
  • 【电影剖析】千钧一发
  • 【openbmc6】entity-manager
  • ROS 与 Ubuntu 版本的对应关系
  • 如何使用电脑连接小米耳机(红米 redmi耳机)
  • 微信二维码扫描登录流程详解
  • redis 如何优雅地进行键设计?
  • Linux学习之Linux系统权限
  • 【论文阅读】Nonparametric clustering of RNA-sequencing data
  • Java IO 流详解:从基础到实战,彻底掌握输入输出编程
  • Linux C 多线程基本操作
  • 基于springboot+vue开发的图书馆座位预约系统【源码+sql+可运行】【50721
  • Djoser 详解
  • 奥比中光深度相机开发
  • Pytorch版本、安装和检验
  • RS485和Modbus
  • 完整的 SquareStudio 注册登录功能实现方案:已经烧录到开发板正常使用
  • 感知机-梯度下降法
  • OpenCV中特征匹配算法GMS(Grid-based Motion Statistics)原理介绍和使用代码示例
  • 使用相机不同曝光时间测试灯光闪烁频率及Ai解释
  • Trae开发uni-app+Vue3+TS项目飘红踩坑
  • hot100回归复习(算法总结1-38)