批量读取邮箱并下载Excel附件,写入Word中,高效办公
off999 2024-12-13 15:36 107 浏览 0 评论
来源:早起Python
作者:陈熹、刘早起
大家好,我是早起。
在之前的Python办公自动化专题系列文章中,我们已经讲解了如何使用Python读取、收发邮件等多个邮件管理操作,有关Python处理Excel和Word相关的理论与实战案例更是介绍了数十篇。
今天就将分享一个更复杂的真实需求,看看如何用Python读取邮件—>下载Excel附件—>将Excel指定内容填写到Word中!
一、需求描述
你在某三甲医院的医务处工作,之前已经发通知让医生们邮件申请外派医院进修,表格 申请.xlsx 类似如下:
在你收到邮件后,需要根据他们的申请表开出相应的Word介绍信:
每个人会单独自己填写好的表格以 “进修申请 xxx” 的邮件标题发到你的邮箱。申请截止日期到了,你打开邮件发现有 300 多人申请!
而手动从邮件中下载附件,打开 Excel 文件并把对应信息填写到 Word,再修改介绍信文件名为 “xxx 进修介绍信” 实在过于繁琐。
这时我们来分析如何用 Python 自动化高效完成上述任务!
二、逻辑梳理
首先我们需要将这个需求拆分成多个小任务,并分析各部分的工作逻辑。
这次的真实需求实际上和之前讲过的案例批量生成多份合同非常类似,不同之处在于需要配合邮件相关的工具完成整个需求。
本需求同样绕不开一个问题:程序如何知道要将某个信息填到何处?
为了解决这个问题,我们需要对模板 介绍信.docx 进行修改,即将需要填写的地方改成某种标识,让程序可以看到标识就明白此处应该放什么信息
采取的策略是:将需要填写的地方改成表中的列名,即:
这样程序通过文本识别就能够定位相应信息并完成替换!
因此,本次需求完整的逻辑包括:
“遍历所有邮件,将标题符合要求的邮件附件下载到指定文件夹中遍历打开文件夹下的所有 Excel 文件获取每个 Excel 表格中的信息,填写至 Word 模板中保存文件到新文件夹中”
三、代码实现
3.1 解析邮件—>下载附件
首先完成第一部分的工作,读取全部邮件:
import keyring
from imbox import Imbox
利用 keyring 库,通过系统密钥环将密码(授权码)预先在本地存储好,后面在代码中调用 keyring 库的方法,通过账号把密码取出来作为变量,降低了密码(授权码)泄露的几率 通过 imbox 库获取附件:
password = keyring.get_password("yagmail","xxx@163.com")
with Imbox('imap.163.com', 'xxx@163.com', password) as imbox:
all_inbox_messages = imbox.messages()
for uid,message in all_inbox_messages:
print(message.attachments)
从需求中我们知道,特定的邮件是以 进修申请 四个字开头的,那么就可以以此为依据作为判断,获取特定邮件的附件:
password = keyring.get_password("yagmail","xxx@163.com")
with Imbox('imap.163.com', 'xxx@163.com', password) as imbox:
all_inbox_messages = imbox.messages()
for uid, message in all_inbox_messages:
if message.subject[:4] == '进修申请':
pass
pass 代码就可以写附件存储了。需要把 Excel 文件存储到指定文件夹中,因此需要先利用 os 库建立文件夹。邮件部分的代码如下:
import keyring
from imbox import Imbox
import os
path = r'C:\xxx'
if not os.path.exists(path + r'\申请表文件夹'):
os.mkdir(path + r'\申请表文件夹')
password = keyring.get_password("yagmail","xxx@163.com")
with Imbox('imap.163.com', 'xxx@163.com', password) as imbox:
all_inbox_messages = imbox.messages()
for uid, message in all_inbox_messages:
if message.subject[:4] == '进修申请':
if message.attachments: # 判断是否存在附件
for attachment in message.attachments:
with open(path + f'\申请表文件夹\\{attachment["filename"]}', 'wb') as file:
file.write(attachment['content'].getvalue())
3.2 读取Excel —> 写入Word
接下来的操作涉及 Excel 读取和 Word 文件的写入,需要导入相应的模块。同时建立新文件夹存放最终的介绍信:
from docx import Document
from openpyxl import load_workbook
if not os.path.exists(path + r'\介绍信文件夹'):
os.mkdir(path + r'\介绍信文件夹')
现在 申请表文件夹 中存放 300 多个 Excel 文件,可以利用 glob 库进行遍历和读取:
import glob
for file in glob.glob(path + r'\申请表文件夹\*.xlsx'):
workbook = load_workbook(file)
sheet = workbook.active
有效信息在第二行,列名(文本替换的依据)在第一行。但考虑到有的申请表可能不按常规,填写了多个人的申请,因此用循环,不局限在第二行:
for file in glob.glob(path + r'\申请表文件夹\*.xlsx'):
workbook = load_workbook(file)
sheet = workbook.active
for table_row in range(2, sheet.max_row + 1): # 考虑到有的申请表可能不按常规,填写了多个人的申请,因此用循环
# 每循环一行实例化一个新的word文件
wordfile = Document(path + r'\新模板.docx')
# 单元格需要逐个遍历,每一个都包含着有用的信息
for table_col in range(1, sheet.max_column + 1):
# 旧的文本也就是列名,已经在模板里填好了,用于文本替换,将row限定在第一行后就是列名
old_text = '#' + str(sheet.cell(row=1, column=table_col).value) + '#'
# 新的文本就是实际的信息,table_col循环到某个数值时,实际的单元格和列名就确定了
new_text = str(sheet.cell(row=table_row, column=table_col).value)
获取到信息以后就可以进行 Word 模板文件的文本替换了,根据其 文档 Document - 段落 Paragraph - 文字块 Run的三级结构,在文字块层面完成替换:
# 文档Document - 段落Paragraph - 文字块Run
all_paragraphs = wordfile.paragraphs
for paragraph in all_paragraphs:
for run in paragraph.runs:
run.text = run.text.replace(old_text, new_text)
介绍信的落款日期是当天的日期,可以考虑借助 datetime 库获取,并在替换新旧文本时同时判断 #今天日期# 这个文本是否存在,存在就替换为真实日期:
run.text = run.text.replace(old_text, new_text)
run.text = run.text.replace('#今天日期#', datetime.date.today())
最后保存即可,文件名中的姓名即为当前循环行的第一个单元格,sheet.cell(row=table_row,column=1).value
完整代码如下:
import keyring
from imbox import Imbox
from docx import Document
from openpyxl import load_workbook
import os
import glob
import datetime
path = r'C:\xxx'
if not os.path.exists(path + r'\申请表文件夹'):
os.mkdir(path + r'\申请表文件夹')
password = keyring.get_password("yagmail", "xxx@163.com")
with Imbox('imap.163.com', 'xxx@163.com', password) as imbox:
all_inbox_messages = imbox.messages()
for uid, message in all_inbox_messages:
if message.subject[:4] == '进修申请':
if message.attachments:
for attachment in message.attachments:
with open(path + f'\申请表文件夹\\{attachment["filename"]}', 'wb') as file:
file.write(attachment['content'].getvalue())
if not os.path.exists(path + r'\介绍信文件夹'):
os.mkdir(path + r'\介绍信文件夹')
for file in glob.glob(path + r'\申请表文件夹\*.xlsx'):
workbook = load_workbook(file)
sheet = workbook.active
for table_row in range(2, sheet.max_row + 1): # 考虑到有的申请表可能不按常规,填写了多个人的申请,因此用循环
# 每循环一行实例化一个新的word文件
wordfile = Document(path + '\新模板.docx')
# 单元格需要逐个遍历,每一个都包含着有用的信息
for table_col in range(1, sheet.max_column + 1):
# 旧的文本也就是列名,已经在模板里填好了,用于文本替换,将row限定在第一行后就是列名
old_text = '#' + str(sheet.cell(row=1, column=table_col).value) + '#'
# 新的文本就是实际的信息,table_col循环到某个数值时,实际的单元格和列名就确定了
new_text = str(sheet.cell(row=table_row, column=table_col).value)
all_paragraphs = wordfile.paragraphs
for paragraph in all_paragraphs:
for run in paragraph.runs:
run.text = run.text.replace(old_text, new_text)
run.text = run.text.replace('#今天日期#', datetime.date.today())
wordfile.save(path + f'\\介绍信文件夹\\{sheet.cell(row=table_row,column=1).value} 进修介绍信.docx')
可以看到,整个复杂的需求就被瓦解成多个问题而成功解决!
相关推荐
- Linux 网络协议栈_linux网络协议栈
-
前言;更多学习资料(包含视频、技术学习路线图谱、文档等)后台私信《资料》免费领取技术点包含了C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,Z...
- 揭秘 BPF map 前生今世_bpfdm
-
1.前言众所周知,map可用于内核BPF程序和用户应用程序之间实现双向的数据交换,为BPF技术中的重要基础数据结构。在BPF程序中可以通过声明structbpf_map_def...
- 教你简单 提取fmpeg 视频,音频,字幕 方法
-
ffmpeg提取视频,音频,字幕方法(HowtoExtractVideo,Audio,SubtitlefromOriginalVideo?)1.提取视频(ExtractVi...
- Linux内核原理到代码详解《内核视频教程》
-
Linux内核原理-进程入门进程进程不仅仅是一段可执行程序的代码,通常进程还包括其他资源,比如打开的文件,挂起的信号,内核内部的数据结构,处理器状态,内存地址空间,或多个执行线程,存放全局变量的数据段...
- Linux C Socket UDP编程详解及实例分享
-
1、UDP网络编程主要流程UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。...
- libevent源码分析之bufferevent使用详解
-
libevent的bufferevent在event的基础上自己维护了一个buffer,这样的话,就不需要再自己管理一个buffer了。先看看structbufferevent这个结构体struct...
- 一次解决Linux内核内存泄漏实战全过程
-
什么是内存泄漏:程序向系统申请内存,使用完不需要之后,不释放内存还给系统回收,造成申请的内存被浪费.发现系统中内存使用量随着时间的流逝,消耗的越来越多,例如下图所示:接下来的排查思路是:1.监控系统中...
- 彻底搞清楚内存泄漏的原因,如何避免内存泄漏,如何定位内存泄漏
-
作为C/C++开发人员,内存泄漏是最容易遇到的问题之一,这是由C/C++语言的特性引起的。C/C++语言与其他语言不同,需要开发者去申请和释放内存,即需要开发者去管理内存,如果内存使用不当,就容易造成...
- linux网络编程常见API详解_linux网络编程视频教程
-
Linux网络编程API函数初步剖析今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作。1、socket(family...
- Linux下C++访问web—使用libcurl库调用http接口发送解析json数据
-
一、背景这两天由于一些原因研究了研究如何在客户端C++代码中调用web服务端接口,需要访问url,并传入json数据,拿到返回值,并解析。 现在的情形是远程服务端的接口参数和返回类型都是json的字符...
- 平衡感知调节:“系统如人” 视角下的架构设计与业务稳定之道
-
在今天这个到处都是数字化的时代,系统可不是一堆冷冰冰的代码。它就像一个活生生的“数字人”,没了它,业务根本转不起来。总说“技术要为业务服务”,但实际操作起来问题不少:系统怎么才能快速响应业务需求?...
- 谈谈分布式文件系统下的本地缓存_什么是分布式文件存储
-
在分布式文件系统中,为了提高系统的性能,常常会引入不同类型的缓存存储系统(算法优化所带来的的效果可能远远不如缓存带来的优化效果)。在软件中缓存存储系统一般可分为了两类:一、分布式缓存,例如:Memca...
- 进程间通信之信号量semaphore--linux内核剖析
-
什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠...
- Qt编写推流程序/支持webrtc265/从此不用再转码/打开新世界的大门
-
一、前言在推流领域,尤其是监控行业,现在主流设备基本上都是265格式的视频流,想要在网页上直接显示监控流,之前的方案是,要么转成hls,要么魔改支持265格式的flv,要么265转成264,如果要追求...
- 30 分钟搞定 SpringBoot 视频推拉流!实战避坑指南
-
30分钟搞定SpringBoot视频推拉流!实战避坑指南在音视频开发领域,SpringBoot凭借其快速开发特性,成为很多开发者实现视频推拉流功能的首选框架。但实际开发中,从环境搭建到流处理优...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)