手把手教你使用python的zipfile模塊巧解word批量生成問題
回復(fù)“書籍”即可獲贈Python從入門到進(jìn)階共10本電子書
大家好,我是宿者朽命,微信名是【??(這是月亮的背面)】,今天給大家分享使用python的zipfile模塊巧解word批量生成問題,這里提供兩種方案給大家參考。
方案一:使用python-docx.Document讀取word文檔
方案二:zipfile巧解word文檔
平臺:windows10 解釋器:python3.7
任務(wù)需求
現(xiàn)有一包含目標(biāo)數(shù)據(jù)的excel文檔,需要將其中的每一行數(shù)據(jù)的對應(yīng)內(nèi)容替換到指定word中,并逐一保存。
任務(wù)拆解
首先查看word文檔格式,可以看到文件后綴為.doc,需要替換的是正文部分紅框中的英文部分。
目標(biāo)數(shù)據(jù)excel文檔,將excel中的對應(yīng)列下的數(shù)據(jù)替換到word文檔中。
excel數(shù)據(jù)比較規(guī)整無需二次處理,如果列名與word文檔中不對應(yīng)或者沒有,則需要調(diào)整或新增。如此一來只要考慮如何讀取doc文件并按一定邏輯進(jìn)行替換即可。
任務(wù)方案
方案一:使用python-docx.Document讀取word文檔
在這次需求之前我并沒有用過python操作word文檔,感謝交流群內(nèi)各位大佬相助,從Python+Excel+Word一秒制作百份合同文章中雖然跟著步伐編寫代碼,而當(dāng)執(zhí)行時(shí)報(bào)錯了,目標(biāo)字符串沒有被替換上等等。
問題一:模塊安裝錯誤,文章中import docx,我誤以為pip install docx就行了,而調(diào)用Document類時(shí),發(fā)現(xiàn)模塊下無該類,遂進(jìn)行百度,應(yīng)當(dāng)時(shí)pip install python-docx,import docx。 問題二:python-docx模塊不能操作doc文檔,上述已提到,本次處理的word文檔為doc后綴,需要將其轉(zhuǎn)換docx后綴方可正常操作,其實(shí)一個文檔通過word軟件進(jìn)行另存為即可,但是在python編程中就顯得不太優(yōu)雅,主要是我太懶了,最多就將目標(biāo)文件路徑拷貝至代碼中,所以使用win32com模塊調(diào)用word程序轉(zhuǎn)換doc文檔為docx文檔。 問題三:Python+Excel+Word一秒制作百份合同這篇文章中是定位到具體文本段在進(jìn)行替換,首次嘗試時(shí),發(fā)現(xiàn)并不能替換成功,將代碼逐步運(yùn)行定位問題所在??梢韵胂笙翫ocument是將整個word文檔分成多個paragraphs,一個paragraphs有很多行,每行有多個文本塊,由于每行中的文本塊的劃分不太明白,中英文輸入法不同方式輸入的中/英文會導(dǎo)致本是一個單詞被拆開,也有可能是該word文檔中含有一定格式造成,如下劃線,在無下劃線的情況下,單詞沒有被分開,嘗試用paragraphs.text進(jìn)行內(nèi)容的替換,文本可以替換成功,但下劃線的格式被丟棄,所以只能采取文本塊下的text方法進(jìn)行替換,在原word文件中用同一種輸入法輸入英文(與excel的列名相對應(yīng),應(yīng)保證該字符串不在word中其他地方出現(xiàn),即中文也是可以的,推薦寫法:#列名#)
將上述問題逐一解決后,輸入目標(biāo)文件路徑及輸入路徑就大功告成了。源碼:
from?copy?import?deepcopy
from?pathlib?import?Path
from?win32com?import?client?as?wc??#?pip?install?pypiwin32
from?docx?import?Document??#?pip?install?python-docx
import?pandas?as?pd
#?python-docx不能處理doc文檔,使用win32com轉(zhuǎn)存為docx文檔
def?doctransform2docx(doc_path):
????docx_path?=?doc_path?+?'x'
????suffix?=?doc_path.split('.')[1]
????assert?'doc'?in?suffix,?'傳入的不是word文檔,請重新輸入!'
????if?suffix?==?'docx':
????????return?Document(doc_path)
????word?=?wc.Dispatch('Word.Application')
????doc?=?word.Documents.Open(doc_path)
????doc.SaveAs2(docx_path,?16)??#?docx為16
????doc.Close()
????word.Quit()
????return?Document(docx_path)
#?替換docx中的特定字符,由于run方法在有格式的docx文件中展示效果很差,故將docx中的文本的需要填充出英文字符占位
def?replace_docx(name,?values,?wordfile,?path_name='Company'):
????wordfile_copy?=?deepcopy(wordfile)??#?防止原文件被篡改,deepcopy為副本
????for?col_name,?value?in?zip(name,?values):
????????if?col_name?==?'Company':
????????????path_name?=?str(value)
????????for?paragraphs?in?wordfile_copy.paragraphs:
????????????for?run?in?paragraphs.runs:
????????????????run.text?=?run.text.replace(col_name,?str(value))
????#?docx文檔替換完畢,另存為,一定要用絕對路徑
????wordfile_copy.save(f'{save_folder}/{path_name}.docx')
if?__name__?==?'__main__':
????#?定義需處理的文件路徑
????doc_path?=?r"D:\solve_path\單位.doc"
????excel_path?=?r"D:\solve_path\信息.xls"
????save_folder?=?Path('D:/docx_save')
????save_folder.mkdir(parents=True,?exist_ok=True)??#?文件夾沒有時(shí)自動創(chuàng)建
????#?獲取excel數(shù)據(jù)
????data?=?pd.read_excel(excel_path)
????wordfile?=?doctransform2docx(doc_path)
????data_save?=?data.apply(lambda?x:?replace_docx(x.index,?x.values,?wordfile),?axis=1)
在我以為大功告成之際,問題來了,原文檔中的方框沒了(漏?。。。?效果圖:
解決了格式卻解決不了特殊字符問題,禿了啊……,我想python-docx中一定有相應(yīng)的解決方案,但是我初次嘗試,對其中源碼部分猶如天書般的存在,在多次調(diào)用方法下發(fā)現(xiàn)其中的一個參數(shù)輸出,wordfile.part.blob:
輸出內(nèi)容讓我想起了之前解密excel時(shí)看到的文件開頭,xml文件,然后首先嘗試替換其中文本,原以為會像run.text = run.text.replace(col_name, str(value))一樣即可,然而報(bào)錯了,禁止修改。
方案二:zipfile巧解word文檔
正當(dāng)我認(rèn)為別無他法時(shí),就此作罷時(shí),百度百科幫助了我:
docx文檔本質(zhì)上就是xml文件,emmmm,很妙,之前為了提取xlsx中的圖片解壓縮過xlsx文件然后提取,果然可行,替換的主體文件就是word文件夾下的document.xml文件
當(dāng)然在代碼編寫前首先嘗試能不能手動復(fù)原為docx,用7z默認(rèn)參數(shù)還原失敗,經(jīng)過多番尋找,用zip類型壓縮即可,軟件不限,手動解壓及替換字符壓縮均成功,開始敲代碼。除習(xí)慣性用pandas讀取excel文件外,也不用安裝其他包,在現(xiàn)用python3.7中均為內(nèi)置包。使用zipfile對壓縮類文件進(jìn)行解壓,文章學(xué)習(xí)來源:
python中如何壓縮和解壓縮文件https://www.cnblogs.com/rongge95500/p/11271764.html
文章中寫得很詳細(xì),我僅把os.path改寫成pathlib。但在對目錄下文件進(jìn)行壓縮還原至docx文檔時(shí)出現(xiàn)了問題:
問題一:文章中的壓縮文件為 zipfile.ZIP_DEFLATED,對遍歷后的所有文件進(jìn)行壓縮至一個目錄下,這就出現(xiàn)了還原后的docx內(nèi)的文件層次不對應(yīng),docx讀取失敗。改用zipfile.zlib.DEFLATED方可成功按層次壓縮。 問題二:zipfile壓縮文件保存時(shí),應(yīng)當(dāng)有文件名及其別名,且別名不能為絕對路徑,為了能正常還原也應(yīng)使用原有名稱,在代碼中為f.write(文件路徑, 文件路徑別名)
源碼:
from?shutil?import?rmtree
import?zipfile
from?copy?import?deepcopy
from?pathlib?import?Path
from?win32com?import?client?as?wc??#?pip?install?pypiwin32
import?pandas?as?pd
#?doc文檔不包含所需xml文件,使用win32com轉(zhuǎn)存為docx文檔
def?doctransform2docx(doc_path):
????docx_path?=?doc_path?+?'x'
????suffix?=?doc_path.split('.')[1]
????assert?'doc'?in?suffix,?'傳入的不是word文檔,請重新輸入!'
????if?suffix?==?'docx':
????????return?Path(doc_path)
????word?=?wc.Dispatch('Word.Application')
????doc?=?word.Documents.Open(doc_path)
????doc.SaveAs2(docx_path,?16)??#?docx為16
????doc.Close()
????word.Quit()
????return?Path(docx_path)
#?docx文檔解壓
def?docx_unzip(docx_path):
????docx_path?=?Path(docx_path)?if?isinstance(docx_path,?str)?else?docx_path
????upzip_path?=?docx_path.with_name(docx_path.stem)
????with?zipfile.ZipFile(docx_path,?'r')?as?f:
????????for?file?in?f.namelist():
????????????f.extract(file,?path=upzip_path)
????xml_path?=?upzip_path.joinpath('word/document.xml')
????with?xml_path.open(encoding='utf-8')?as?f:
????????xml_file?=?f.read()
????return?upzip_path,?xml_path,?xml_file
#?講文件夾中的所有文件壓縮成docx文檔
def?docx_zipped(docx_path,?zipped_path):
????docx_path?=?Path(docx_path)?if?isinstance(docx_path,?str)?else?docx_path
????with?zipfile.ZipFile(zipped_path,?'w',?zipfile.zlib.DEFLATED)?as?f:
????????for?file?in?docx_path.glob('**/*.*'):
????????????f.write(file,?file.as_posix().replace(docx_path.as_posix()?+?'/',?''))
#?刪除生成的解壓文件夾
def?remove_folder(path):
????path?=?Path(path)?if?isinstance(path,?str)?else?path
????if?path.exists():
????????rmtree(path)
????else:
????????raise?"系統(tǒng)找不到指定的文件"
????????????
#?替換docx中的特定字符,重新保存document.xml至需要壓縮的目錄下
def?replace_docx(name,?values,?xml_file,?xml_path,?unzip_path,?path_name='Company'):
????xml_path?=?Path(xml_path)?if?isinstance(xml_path,?str)?else?xml_path
????xml_file_copy?=?deepcopy(xml_file)??#?深復(fù)制xml內(nèi)容
????for?col_name,?value?in?zip(name,?values):
????????if?col_name?==?'Company':
????????????path_name?=?str(value)
????????xml_file_copy?=?xml_file_copy.replace(col_name,?str(value))
????with?xml_path.open(mode='w',?encoding='utf-8')?as?f:
????????f.write(xml_file_copy)
????#?xml文檔替換完畢,通過zipfile重新壓縮另存為docx文檔
????docx_zipped(unzip_path,?f'{save_folder}/{path_name}.docx')
if?__name__?==?'__main__':
????#?定義需處理的文件路徑
????doc_path?=?r"D:\solve_path\單位.doc"
????excel_path?=?r"D:\solve_path\信息.xls"
????save_folder?=?Path('D:/docx_save')
????save_folder.mkdir(parents=True,?exist_ok=True)??#?文件夾沒有時(shí)自動創(chuàng)建
????#?獲取excel數(shù)據(jù)
????data?=?pd.read_excel(excel_path)
????docx_path?=?doctransform2docx(doc_path)
????unzip_path,?xml_path,?xml_file?=?docx_unzip(docx_path)
????data_save?=?data.apply(lambda?x:?replace_docx(x.index,?x.values,?xml_file,?xml_path,?unzip_path),?axis=1)
????remove_folder(unzip_path)
打開生成的文件,方框沒有消失,下劃線也在。
總結(jié)
經(jīng)過幾番嘗試后,也是我的學(xué)識不深,在跌跌撞撞中找到一種既能替換docx中的字符串也不會改變原有格式的方案,相信一定會有更好的方案,只是此時(shí)我沒有找到,時(shí)間是不停地向前的,我也不應(yīng)落下,以求共同富貴。如對此有疑問歡迎評論區(qū)留言。
PS: 如果電腦安裝的是WPS,可以嘗試手動轉(zhuǎn)換doc格式為docx格式,再進(jìn)行批量操作。
于二零二一年十一月二十八日作
版權(quán)聲明:本文為CSDN博主「宿者朽命」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_46281427/article/details/121588786
小伙伴們,快快用實(shí)踐一下吧!如果在學(xué)習(xí)過程中,有遇到任何問題,歡迎加我好友,我拉你進(jìn)Python學(xué)習(xí)交流群共同探討學(xué)習(xí)。
-------------------?End?-------------------
往期精彩文章推薦:
補(bǔ)充篇:盤點(diǎn)6種使用Python批量合并同一文件夾內(nèi)所有子文件夾下的Excel文件內(nèi)所有Sheet數(shù)據(jù)
盤點(diǎn)4種使用Python批量合并同一文件夾內(nèi)所有子文件夾下的Excel文件內(nèi)所有Sheet數(shù)據(jù)
手把手教你使用Python網(wǎng)絡(luò)爬蟲實(shí)現(xiàn)郵件定時(shí)發(fā)送(附源碼)

歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Python學(xué)習(xí)群請?jiān)诤笈_回復(fù)【入群】
萬水千山總是情,點(diǎn)個【在看】行不行
/今日留言主題/
隨便說一兩句吧~~
