[Python] 自動化辦公 郵件/Excel/Word交互快速生成介紹信

轉(zhuǎn)載請注明:陳熹 chenx6542@foxmail.com (簡書號:半為花間酒)
若公眾號內(nèi)轉(zhuǎn)載請聯(lián)系公眾號:早起Python

這篇文章能學到的主要內(nèi)容:

  1. imbox 讀取郵件解析附件
  2. openpyxlpython-docx 對文件的交互操作

https://pan.baidu.com/s/1QKXBIPvOEGurpqbe4KiGTA 提取碼:j2xp

一、需求描述

你在某三家醫(yī)院的醫(yī)務(wù)處工作,之前已經(jīng)發(fā)通知讓醫(yī)生們申請外派 A 醫(yī)院進修,表格 申請.xlsx 如下:

你需要根據(jù)他們的申請表開出相應(yīng)的介紹信:

每個人會單獨自己填寫好的表格以 “進修申請 xxx” 的郵件標題發(fā)到你的郵箱。申請截止日期到了,你打開郵件發(fā)現(xiàn)有 300 多人申請,而你覺得從郵件中下載附件,打開 Excel 文件并把對應(yīng)信息填寫到 Word,再修改介紹信文件名為 “xxx 進修介紹信” 實在過于繁瑣,你希望借助 Python 自動化高效完成上述任務(wù)

二、邏輯梳理

這次的真實需求實際上和之前的推文 批量生成多份合同:http://www.itdecent.cn/p/3ee47f594d81 非常類似,不同之處在于需要配合郵件相關(guān)的工具完成整個需求。本需求同樣繞不開一個問題:程序如何知道要將某個信息填到何處?為了解決這個問題,我們需要對模板 介紹信.docx 進行修改,即將需要填寫的地方改成某種標識,讓程序可以“看到標識就明白此處應(yīng)該放什么信息”

采取的策略是:將需要填寫的地方改成表中的列名,即:

這樣程序通過文本識別就能夠定位相應(yīng)信息并完成替換

本需求完整的邏輯包括:

  1. 遍歷所有郵件,將標題符合要求的郵件附件下載到指定文件夾中
  2. 遍歷打開文件夾下的所有 Excel 文件
  3. 獲取每個 Excel 表格中的信息,填寫至 Word 模板中
  4. 保存文件到新文件夾中

三、代碼實現(xiàn)

1. 解析郵件

首先完成第一部分的工作,讀取全部郵件:

import keyring
from imbox import Imbox

利用 keyring 庫,通過系統(tǒng)密鑰環(huán)將密碼(授權(quán)碼)預(yù)先在本地存儲好,后面在代碼中調(diào)用 keyring 庫的方法,通過賬號把密碼取出來作為變量,降低了密碼(授權(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ù)作為判斷,獲取特定郵件的附件:

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())

2. Excel 和 Word 文件交互

接下來的操作涉及 Excel 讀取和 Word 文件的寫入,需要導入相應(yīng)的模塊。同時建立新文件夾存放最終的介紹信:

from docx import Document
from openpyxl import load_workbook

if not os.path.exists(path + r'\介紹信文件夾'):
    os.mkdir(path + r'\介紹信文件夾')

現(xiàn)在 申請表文件夾 中存放 300 多個 Excel 文件,可以利用 glob 庫進行遍歷和讀?。?/p>

import glob

for file in glob.glob(path + r'\申請表文件夾\*.xlsx'):
    workbook = load_workbook(file)
    sheet = workbook.active

有效信息在第二行,列名(文本替換的依據(jù))在第一行。但考慮到有的申請表可能不按常規(guī),填寫了多個人的申請,因此用循環(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ī),填寫了多個人的申請,因此用循環(huán)
        # 每循環(huán)一行實例化一個新的word文件
        wordfile = Document(path + r'\新模板.docx')
        # 單元格需要逐個遍歷,每一個都包含著有用的信息
        for table_col in range(1, sheet.max_column + 1):
            # 舊的文本也就是列名,已經(jīng)在模板里填好了,用于文本替換,將row限定在第一行后就是列名
            old_text = '#' + str(sheet.cell(row=1, column=table_col).value) + '#'
            # 新的文本就是實際的信息,table_col循環(huán)到某個數(shù)值時,實際的單元格和列名就確定了
            new_text = str(sheet.cell(row=table_row, column=table_col).value)

獲取到信息以后就可以進行 Word 模板文件的文本替換了,根據(jù)其 文檔 Document - 段落 Paragraph - 文字塊 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)

介紹信的落款日期是當天的日期,可以考慮借助 datetime 庫獲取,并在替換新舊文本時同時判斷 #今天日期# 這個文本是否存在,存在就替換為真實日期:

                run.text = run.text.replace(old_text, new_text)
                run.text = run.text.replace('#今天日期#', datetime.date.today())

最后保存即可,文件名中的姓名即為當前循環(huán)行的第一個單元格,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):  # 考慮到有的申請表可能不按常規(guī),填寫了多個人的申請,因此用循環(huán)
        # 每循環(huán)一行實例化一個新的word文件
        wordfile = Document(path + '\新模板.docx')
        # 單元格需要逐個遍歷,每一個都包含著有用的信息
        for table_col in range(1, sheet.max_column + 1):
            # 舊的文本也就是列名,已經(jīng)在模板里填好了,用于文本替換,將row限定在第一行后就是列名
            old_text = '#' + str(sheet.cell(row=1, column=table_col).value) + '#'
            # 新的文本就是實際的信息,table_col循環(huán)到某個數(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} 進修介紹信.docx')

整個復雜的需求就被瓦解成多個問題而成功解決!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容