资料来源:早起python
作者:陈曦、刘晨起床
你好,我起得很早。
在前面的Python Office Automation系列文章中,我们讨论了如何使用多个邮件管理任务,包括读取Python、发送和接收邮件。Python介绍了数十篇与Excel和Word处理相关的理论和实践案例。
今天,我们将分享更复杂的实际需求,用Python阅读消息,下载Excel附件,并将Excel指定填充到Word中!
一、需求说明
你在某三甲医院的医疗处工作之前,已经向医生们发出了申请外派医院进修的通知,表格是。申请xlsx如下:
你收到邮件后,要根据他们的申请打开相应的Word介绍信。
我会以“研修申请XXX”为题,通过电子邮件发送各自亲自填写的表格。申请截止日期到了,你打开邮件发现有300多人申请了!
手动从邮件下载附件,打开Excel文件,将相应信息填写在Word上,将介绍信文件名修改为“XXX研修介绍信”,太麻烦了。
现在,让我们分析一下如何通过Python自动化高效地完成这些任务!
二、逻辑梳理
首先,必须将这一要求分解为多个小任务,并分析每个部分的任务逻辑。
这次实际需求实际上与上述案例大量生成多个合同非常相似。但是,不同的是,除了与邮件相关的工具外,还必须完成全部要求。
这个要求也绕不开一个问题:程序如何知道该在哪里填充哪些信息?(阿尔伯特爱因斯坦)。
为了解决这个问题,我们写了模板介绍信。要修改docx,把需要写的地方换成什么docx,这样程序才能看到docx,知道应该在这里放什么信息。(大卫亚设)。
策略是将需要填写的部分更改为表中的列名。
这样,程序就可以通过文本识别找到相应的信息,完成替换!
因此,这一需求的完整逻辑如下:
“遍历所有邮件,将符合标题的邮件附件下载到指定文件夹中,遍历打开的文件夹下的所有Excel文件,导入每个Excel表单的信息,将文件保存到Word模板中,保存到新文件夹中”
三、代码的实现
3.1确认消息-下载附件
首先完成第一部分的工作,然后阅读所有邮件。
Importkeyring
FromimboximportImbox
使用Keyring库,您可以通过系统密钥环在本地预先存储密码(验证码),以后在代码中调用keyring库方法,通过帐户将密码提取到变量中,从而降低密码(验证码)泄漏的可能性,从而通过imbox库获取附件。
password=keyring . get _ password(' yagmail ',' XXX @ 163.com ')
Withimbox ('imap.163.com ',' XXX @ 163.com ',password) ASI mbox 3360
all _ inbox _ messages=imbox . messages()
Foruid、message inall _ inbox _ messages 3360
Prin)
从需求可以看出,特定邮件以进修申请四个词开始,因此可以根据此收到特定邮件的附件。
password=keyring . get _ password(' yagmail ',' XXX @ 163.com ')
Withimbox ('imap.163.com ',' XXX @ 163.com ',password) ASI mbox 3360
all _ inbox _ messages=imbox . messages()
Foruid、message inall _ inbox _ messages 3360
Ifme[:4]='进修申请' :
passpass 代码就可以写附件存储了。需要把 Excel 文件存储到指定文件夹中,因此需要先利用 os 库建立文件夹。邮件部分的代码如下:
import keyring
from imbox import Imbox
import os
path = r'C:\xxx'
if not os.(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 me[:4] == '进修申请':
if me: # 判断是否存在附件
for attachment in me:
with open(path + f'\申请表文件夹\\{attachment["filename"]}', 'wb') as file:
(attachment['content'].getvalue())
3.2 读取Excel —> 写入Word
接下来的操作涉及 Excel 读取和 Word 文件的写入,需要导入相应的模块。同时建立新文件夹存放最终的介绍信:
from docx import Document
from openpyxl import load_workbook
if not os.(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, + 1): # 考虑到有的申请表可能不按常规,填写了多个人的申请,因此用循环
# 每循环一行实例化一个新的word文件
wordfile = Document(path + r'\新模板.docx')
# 单元格需要逐个遍历,每一个都包含着有用的信息
for table_col in range(1, + 1):
# 旧的文本也就是列名,已经在模板里填好了,用于文本替换,将row限定在第一行后就是列名
old_text = '#' + str(row=1, column=table_col).value) + '#'
# 新的文本就是实际的信息,table_col循环到某个数值时,实际的单元格和列名就确定了
new_text = str(row=table_row, column=table_col).value)
获取到信息以后就可以进行 Word 模板文件的文本替换了,根据其 文档 Document - 段落 Paragraph - 文字块 Run的三级结构,在文字块层面完成替换:
# 文档Document - 段落Paragraph - 文字块Run
all_paragraphs = word
for paragraph in all_paragraphs:
for run in :
run.text = run.(old_text, new_text)
介绍信的落款日期是当天的日期,可以考虑借助 datetime 库获取,并在替换新旧文本时同时判断 #今天日期# 这个文本是否存在,存在就替换为真实日期:
run.text = run.(old_text, new_text)
run.text = run.('#今天日期#', da())
最后保存即可,文件名中的姓名即为当前循环行的第一个单元格,(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 + 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 me[:4] == '进修申请':
if me:
for attachment in me:
with open(path + f'\申请表文件夹\\{attachment["filename"]}', 'wb') as file:
(attachment['content'].getvalue())
if not os.(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, + 1): # 考虑到有的申请表可能不按常规,填写了多个人的申请,因此用循环
# 每循环一行实例化一个新的word文件
wordfile = Document(path + '\新模板.docx')
# 单元格需要逐个遍历,每一个都包含着有用的信息
for table_col in range(1, + 1):
# 旧的文本也就是列名,已经在模板里填好了,用于文本替换,将row限定在第一行后就是列名
old_text = '#' + str(row=1, column=table_col).value) + '#'
# 新的文本就是实际的信息,table_col循环到某个数值时,实际的单元格和列名就确定了
new_text = str(row=table_row, column=table_col).value)
all_paragraphs = word
for paragraph in all_paragraphs:
for run in :
run.text = run.(old_text, new_text)
run.text = run.('#今天日期#', da())
word(path + f'\\介绍信文件夹\\{(row=table_row,column=1).value} 进修介绍信.docx')
可以看到,整个复杂的需求就被瓦解成多个问题而成功解决!