99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁大數(shù)據(jù)時代python+Excel+Word一秒制作百份合同
python+Excel+Word一秒制作百份合同
2020-08-11
收藏

python確實是一款很實用的數(shù)據(jù)分析工具,尤其是在處理重復(fù)性工作方面。小編今天給大家推薦的這篇文章就是關(guān)于python自動化處理合同的,希望能幫助大家解放雙手,實現(xiàn)合同的自動化處理。

文章來源: 早起Python

作者:陳熹

前言

大家好,又到了Python辦公自動化系列。

今天我們繼續(xù)分享一個真實的辦公自動化需求:如何使Python+Excel+Word批量生成指定格式內(nèi)容的合同。

主要涉及的知識點有:openpyxl模塊的綜合運用與Word文檔的兩種遍歷邏輯。

需求描述

你是乙方建筑公司,手上有一份空白合同模板的Word文件,如下圖:

另外還有一份Excel合同信息表,其中是所有甲方(發(fā)包人)在合同中需要填寫的內(nèi)容

可見一行為一個公司的全部信息,現(xiàn)在需要把Excel中每一個公司的信息填入空白Word合同模板中,生成各公司的合同,最終結(jié)果如下

步驟分析

原本我們需要將Excel匯總表中每一行的信息填進word模板中,生成相應(yīng)的合同。

現(xiàn)在我們需要交給Python來實現(xiàn),就引出了一個問題:程序如何知道要將某個信息填到哪個下劃線? 為了解決這個問題,我們需要對模板進行修改。

即將下劃線改成某種標(biāo)識,讓程序可以看到標(biāo)識就明白此處應(yīng)該放什么信息,這里采取的策略是:將需要填寫的下劃線改成匯總表中的列名,即下圖所示

這樣程序就可以識別需要填寫什么內(nèi)容了。所謂的識別在這里可以換一個特別簡單的詞,即文本替換。只要檢索到#xxxx#(excel中的列名),把這個替換成具體的信息就可以了。

出于這種策略,列名就需要用#xxxx#的格式,否則正常的無關(guān)文本中的信息也會被替換,就破壞了原有的需求,最后模板被修改成如下:

通過Excel表我們可以看到,一行為一個公司的信息,而每一列的列名就存在于模板中,用各個公司的實際信息替換到模板中的列名(程序識別和文本替換的依據(jù))

用這樣的方法就可以完成這個需求。整個大需求的實現(xiàn)可以按照下面的步驟:

分析后的步驟:

將 空白合同 調(diào)整成 合同模板,需要填寫的下劃線改成專屬的列名

打開Excel表,按行循環(huán),然后按單元格逐個循環(huán)各個信息,每個信息都找到模板中存在的對應(yīng)列名并將其替換(如果不理解下文還有解釋)

每次循環(huán)完一行的全部單元格后保存合同,生存各個公司單獨的合同

分析清楚后邏輯就非常簡單了,但有一個隱含的知識點沒有提到,讓我們邊寫代碼邊說!

代碼實現(xiàn)

首先導(dǎo)入模塊,設(shè)置路徑,建立文件夾,本例中涉及Excel表的打開和Word的創(chuàng)建,因此需要從openpyxl導(dǎo)入load_workbook,而Word無論打開還是創(chuàng)建,用docx模塊的Document均可

from docx import Document
from openpyxl import load_workbook
# 利用os模塊建立文件夾,用于存放生成的合同
import os

# 給定合同模板和匯總表所在的文件夾路徑,方便復(fù)用
path = r'C:\Users\chenx\Desktop\合同'

# 結(jié)合路徑判斷生成文件夾,規(guī)避程序報錯而終止的風(fēng)險
if not os.path.exists(path + '/' + '全部合同'):
    os.mkdir(path + '/' + '全部合同')

接著打開Excel文件

workbook = load_workbook(path + '/' + '合同信息表.xlsx')
sheet = workbook.active

現(xiàn)在遍歷Excel,生成合同。前面也反復(fù)提到,Excel的每一行是一份特定合同的信息,因此docx針對Word文件的實例化和保存一定是在循環(huán)體里的,而不像Excel的實例化是在循環(huán)體外面

# 有效信息行是從第二行開始的,第二行是表頭,包含列名,也是文本替換的依據(jù)
for table_row in range(2, sheet.max_row + 1):
    # 每循環(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)
        # 加上這個判斷是因為日期信息讀進程序是“日期 時間”格式的,如果要保留日期信息可以用字符串方法或者用time/datetime模塊處理
        if ' ' in new_text:
            new_text = new_text.split()[0]

通過下圖進一步理解這個替換:

例如程序已經(jīng)進入第3個循環(huán)(循環(huán)到第3個公司),針對單元格的循環(huán)進入第4個循環(huán),那么此時獲取的實際值是建設(shè)C公園,對應(yīng)的列名是#工程內(nèi)容#。

此時就明確了需要被替換的內(nèi)容了,只要在模板中找到#工程內(nèi)容#把它替換為建設(shè)C公園即可!了解了這個替換后,下一步就是遍歷Word模板,找到對應(yīng)列名替換!

之前我們說過docx模塊,Word文本存在文檔Document-段落Paragraph-文字塊Run的三級結(jié)構(gòu),需要遍歷文本可以用以下代碼:

all_paragraphs = wordfile.paragraphs
for paragraph in all_paragraphs:
    print(paragraph.text)
    for run in paragraph.runs:
        print(run.text)

針對段落和文字塊均可用.text獲取到文字信息。本需求隱含的陷阱就在這里,注意一下合同最后需要填寫的內(nèi)容:

這部分內(nèi)容如果用上述代碼是遍歷不到的。為什么?因為這是Word文檔中的表格!

遍歷表格需要有專門的遍歷邏輯:文檔Document-表格Table-行Row/列Column-單元格Cell,遍歷表格中文本的代碼如下:

all_tables = wordfile.tables
for table in all_tables:
    # 也可按列遍歷
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)

有了這些補充的知識之后,本案例中最核心的代碼就可以這么寫

for table_row in range(2, sheet.max_row + 1):
    wordfile = Document(path + '/' + '合同模板.docx')
    for table_col in range(1, sheet.max_column + 1):
        old_text = str(sheet.cell(row=1, column=table_col).value)
        new_text = str(sheet.cell(row=table_row, column=table_col).value)
        if ' ' in new_text:
            new_text = new_text.split()[0]
        
        # 文檔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)

        # 文檔Document - 表格Table - 行Row/列Column - 單元格Cell
        all_tables = wordfile.tables
        for table in all_tables:
            for row in table.rows:
                for cell in row.cells:
                    cell.text = cell.text.replace(old_text, new_text)

    # 獲取公司名用以生成合同的名稱
    company = str(sheet.cell(row=table_row, column=1).value)
    wordfile.save(path + '/' + f'全部合同/{company}合同.docx')

寫在最后

本次的案例具有較強的實用性,并且需求可以延伸成為:將一份信息匯總表Excel中的每一個單獨信息(每一行或者每一列為個人、公司或者其他的信息)填寫到指定的模板Eord中,生成單獨的文檔,不過在寫自動化腳本之前也要先拆分任務(wù),明確思路再進行!

數(shù)據(jù)分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個參數(shù)驗證碼對象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺檢測極驗服務(wù)器是否宕機 new_captcha: data.new_captcha, // 用于宕機時表示是新驗證碼的宕機 product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計時完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }