大家好,我是早起。
在之前的辦公自動(dòng)化專題系列文章中,我們已經(jīng)講解了如何使用讀取、收發(fā)郵件等多個(gè)郵件管理操作,有關(guān)處理Excel和Word相關(guān)的理論與實(shí)戰(zhàn)案例更是介紹了數(shù)十篇。
今天就將分享一個(gè)更復(fù)雜的真實(shí)需求word文檔中附件打不開,看看如何用讀取郵件—>下載Excel附件—>將Excel指定內(nèi)容填寫到Word中!
一、需求描述
你在某三甲醫(yī)院的醫(yī)務(wù)處工作,之前已經(jīng)發(fā)通知讓醫(yī)生們郵件申請外派醫(yī)院進(jìn)修,表格 申請.xlsx 類似如下:
在你收到郵件后,需要根據(jù)他們的申請表開出相應(yīng)的Word介紹信:
每個(gè)人會(huì)單獨(dú)自己填寫好的表格以 “進(jìn)修申請 xxx” 的郵件標(biāo)題發(fā)到你的郵箱。申請截止日期到了,你打開郵件發(fā)現(xiàn)有 300 多人申請!
而手動(dòng)從郵件中下載附件,打開 Excel 文件并把對應(yīng)信息填寫到 Word,再修改介紹信文件名為 “xxx 進(jìn)修介紹信” 實(shí)在過于繁瑣。
這時(shí)我們來分析如何用 自動(dòng)化高效完成上述任務(wù)!
二、邏輯梳理
首先我們需要將這個(gè)需求拆分成多個(gè)小任務(wù),并分析各部分的工作邏輯。
這次的真實(shí)需求實(shí)際上和之前講過的案例非常類似,不同之處在于需要配合郵件相關(guān)的工具完成整個(gè)需求。
本需求同樣繞不開一個(gè)問題:程序如何知道要將某個(gè)信息填到何處?
為了解決這個(gè)問題,我們需要對模板 介紹信.docx 進(jìn)行修改,即將需要填寫的地方改成某種標(biāo)識,讓程序可以看到標(biāo)識就明白此處應(yīng)該放什么信息
采取的策略是:將需要填寫的地方改成表中的列名,即:
這樣程序通過文本識別就能夠定位相應(yīng)信息并完成替換!
因此,本次需求完整的邏輯包括:
“遍歷所有郵件,將標(biāo)題符合要求的郵件附件下載到指定文件夾中遍歷打開文件夾下的所有 Excel 文件獲取每個(gè) Excel 表格中的信息,填寫至 Word 模板中保存文件到新文件夾中”三、代碼實(shí)現(xiàn)3.1 解析郵件—>下載附件
首先完成第一部分的工作,讀取全部郵件:
import?keyring
from?imbox?import?Imbox
利用 庫,通過系統(tǒng)密鑰環(huán)將密碼(授權(quán)碼)預(yù)先在本地存儲好,后面在代碼中調(diào)用 庫的方法,通過賬號把密碼取出來作為變量,降低了密碼(授權(quán)碼)泄露的幾率通過 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)
從需求中我們知道,特定的郵件是以 進(jìn)修申請 四個(gè)字開頭的,那么就可以以此為依據(jù)作為判斷,獲取特定郵件的附件:
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]?==?'進(jìn)修申請':
??????????????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]?==?'進(jìn)修申請':
??????????????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 文件的寫入,需要導(dǎo)入相應(yīng)的模塊。同時(shí)建立新文件夾存放最終的介紹信:
from?docx?import?Document
from?openpyxl?import?load_workbook
if?not?os.path.exists(path?+?r'\介紹信文件夾'):
????os.mkdir(path?+?r'\介紹信文件夾')
現(xiàn)在 申請表文件夾 中存放 300 多個(gè) Excel 文件,可以利用 glob 庫進(jìn)行遍歷和讀取:
import?glob
for?file?in?glob.glob(path?+?r'\申請表文件夾\*.xlsx'):
????workbook?=?load_workbook(file)
????sheet?=?workbook.active
有效信息在第二行,列名(文本替換的依據(jù))在第一行。但考慮到有的申請表可能不按常規(guī),填寫了多個(gè)人的申請,因此用循環(huán),不局限在第二行:
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):??#?考慮到有的申請表可能不按常規(guī),填寫了多個(gè)人的申請,因此用循環(huán)
????????#?每循環(huán)一行實(shí)例化一個(gè)新的word文件
????????wordfile?=?Document(path?+?r'\新模板.docx')
????????#?單元格需要逐個(gè)遍歷,每一個(gè)都包含著有用的信息
????????for?table_col?in?range(1,?sheet.max_column?+?1):
????????????#?舊的文本也就是列名,已經(jīng)在模板里填好了,用于文本替換,將row限定在第一行后就是列名
????????????old_text?=?'#'?+?str(sheet.cell(row=1,?column=table_col).value)?+?'#'
????????????#?新的文本就是實(shí)際的信息,table_col循環(huán)到某個(gè)數(shù)值時(shí),實(shí)際的單元格和列名就確定了
????????????new_text?=?str(sheet.cell(row=table_row,?column=table_col).value)
獲取到信息以后就可以進(jìn)行 Word 模板文件的文本替換了,根據(jù)其 文檔 - 段落 - 文字塊 Run的三級結(jié)構(gòu),在文字塊層面完成替換:
#?文檔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)
介紹信的落款日期是當(dāng)天的日期,可以考慮借助 庫獲取,并在替換新舊文本時(shí)同時(shí)判斷 #今天日期# 這個(gè)文本是否存在,存在就替換為真實(shí)日期:
????????????????run.text?=?run.text.replace(old_text,?new_text)
????????????????run.text?=?run.text.replace('#今天日期#',?datetime.date.today())
最后保存即可,文件名中的姓名即為當(dāng)前循環(huán)行的第一個(gè)單元格,sheet.cell(row=,=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]?==?'進(jìn)修申請':
??????????????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):??#?考慮到有的申請表可能不按常規(guī),填寫了多個(gè)人的申請,因此用循環(huán)
????????#?每循環(huán)一行實(shí)例化一個(gè)新的word文件
????????wordfile?=?Document(path?+?'\新模板.docx')
????????#?單元格需要逐個(gè)遍歷,每一個(gè)都包含著有用的信息
????????for?table_col?in?range(1,?sheet.max_column?+?1):
????????????#?舊的文本也就是列名,已經(jīng)在模板里填好了,用于文本替換,將row限定在第一行后就是列名
????????????old_text?=?'#'?+?str(sheet.cell(row=1,?column=table_col).value)?+?'#'
????????????#?新的文本就是實(shí)際的信息,table_col循環(huán)到某個(gè)數(shù)值時(shí),實(shí)際的單元格和列名就確定了
????????????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}?進(jìn)修介紹信.docx')
可以看到,整個(gè)復(fù)雜的需求就被瓦解成多個(gè)問題而成功解決!
“
「早起」推出的辦公自動(dòng)化系列,旨在用解決日常辦公中的繁瑣、復(fù)雜需求,從而解放雙手,高效辦公,每一篇中的案例均是來自真實(shí)的辦公場景中!
如果你有辦公自動(dòng)化的需求word文檔中附件打不開,但是無法解決,可以添加早小起微信()聯(lián)系我們處理!
”