最全總結(jié) | 聊聊 Python 辦公自動化之 Word(下)
點擊上方“AirPython”,選擇“加為星標”
第一時間關(guān)注 Python 技術(shù)干貨!

1. 前言
關(guān)于 Word 文檔的讀寫,前面兩篇文章分別進行了一次全面的總結(jié)
最全總結(jié) | 聊聊 Python 辦公自動化之 Word(上)
最全總結(jié) | 聊聊 Python 辦公自動化之 Word(中)
本篇文章作為一個辦公自動化 Word 篇的一個補充,寫寫幾個比較實用的辦公場景
包含:
頁眉頁腳處理
合并多個文檔
新增數(shù)字索引
doc 批量轉(zhuǎn) docx
對比文檔差異性
特別內(nèi)容標注
替換文字內(nèi)容
2. 頁眉頁腳
每一個頁面章節(jié)都包含:頁眉頁腳
它可以單獨設(shè)置,每個頁面都不一樣;也可以全部設(shè)置成與首頁一樣
這個功能,由章節(jié)對象中的屬性?different_first_page_header_footer 來控制
當值為 True 時,代表頁眉頁腳不同于首頁,每個頁面章節(jié)的頁眉、頁腳都可以單獨設(shè)置
當值為 False 時,所有頁面的頁眉、頁腳都一樣
#?1、獲取待處理頁眉、頁腳的章節(jié)
header?=?self.doc.sections[0].header
footer?=?self.doc.sections[0].footer
#?True?if?this?section?displays?a?distinct?first-page?header?and?footer
# True:頁眉頁腳不同于首頁,每個頁面章節(jié)的頁眉頁腳單獨設(shè)置
# False:每個頁面的頁眉頁腳相同
self.doc.sections[0].different_first_page_header_footer?=?True
添加頁眉頁腳包含兩種,分別是:普通頁眉頁腳、自定義樣式的頁眉頁腳
1 - 普通頁眉頁腳
def?add_norm_header_and_footer(header,?footer,?header_content,?footer_content):
????"""
????增加一個普通的頁眉、頁腳,并居中顯示
????:param?header_content:
????:param?footer_content:
????:return:
????"""
????#?新增/修改頁眉、頁腳
????#?注意:一般頁眉、頁腳里只有一個段落
????header.paragraphs[0].text?=?header_content
????footer.paragraphs[0].text?=?footer_content
????#?居中顯示
????header.paragraphs[0].alignment?=?WD_PARAGRAPH_ALIGNMENT.CENTER
????footer.paragraphs[0].alignment?=?WD_PARAGRAPH_ALIGNMENT.CENTER
#?2、新增頁眉
#?2.1?普通的頁眉、頁腳
add_norm_header_and_footer(header,?footer,?"我是一個頁眉",?"我是一個頁腳")
2 - 自帶樣式的頁眉頁腳
def?add_custom_style_header_and_footer(header,?footer,?header_content,?footer_content,?style):
????"""
????新增自定義的頁眉、頁腳
????:param?header:
????:param?footer:
????:param?header_content:
????:param?footer_content:
????:param?style:
????:return:
????"""
????#?注意:style_type=2,否則會報錯
????header.paragraphs[0].add_run(header_content,?style)
????footer.paragraphs[0].add_run(footer_content,?style)
#?2.2?自帶樣式的頁眉、頁腳
#?創(chuàng)建一個樣式
style_paragraph?=?create_style(document=self.doc,?style_name="style5",?style_type=2,?font_size=30,
???????????????????????????????font_color=[0xff,?0x00,?0x00],?align=WD_PARAGRAPH_ALIGNMENT.CENTER)
add_custom_style_header_and_footer(header,?footer,?"我是頁眉2",?"我是頁腳2",?style_paragraph)
如果想將文檔中所有的頁眉、頁腳刪除掉,只需要 2 個步驟:
遍歷文檔中所有頁面章節(jié),將其?different_first_page_header_footer 屬性值設(shè)置為 False
設(shè)置章節(jié)對象頁眉頁腳的?is_linked_to_previous 屬性值為 True
PS:當 is_linked_to_previous 設(shè)置為 True 時,頁眉頁腳會被刪除
def?remove_all_header_and_footer(doc):
????"""
????刪除文檔中所有頁眉和頁腳
????:param?doc:
????:return:
????"""
????for?section?in?doc.sections:
????????section.different_first_page_header_footer?=?False
????????#?當is_linked_to_previous設(shè)置為True時,頁眉頁腳會被刪除
????????section.header.is_linked_to_previous?=?True
????????section.footer.is_linked_to_previous?=?True
3. 合并多個文檔
日常工作中,經(jīng)常會遇到將多個 Word 文檔合并成一個文件的需求
這里,可以使用另外一個 Python 依賴庫:docxcompose
#?合并多個文件的依賴庫
#?pip3?install?docxcompose
使用也非常簡單,只需要下面 4?行代碼,就能將多個文件進行合并,生成到一個新的文件中去
from?docxcompose.composer?import?Composer
def?compose_files(self,?files,?output_file_path):
????"""
????合并多個word文件到一個文件中
????:param?files:待合并文件的列表
????:param?output_file_path?新的文件路徑
????:return:
????"""
????composer?=?Composer(Document())
????for?file?in?files:
????????composer.append(Document(file))
????#?保存到新的文件中
????composer.save(output_file_path)4. 新增數(shù)字索引
我們經(jīng)常需要在文檔頁腳處添加頁面數(shù)字索引,可惜 python-docx 并沒有提供現(xiàn)有方法
但是,在?stackoverflow 上找到實現(xiàn)的方式
https://stackoverflow.com/questions/56658872/add-page-number-using-python-docx?rq=1
from?docx.oxml.xmlchemy?import?BaseOxmlElement,?ZeroOrOne,?ZeroOrMore,?OxmlElement
from?docx.enum.text?import?WD_PARAGRAPH_ALIGNMENT
from?docx.oxml?import?ns
def?create_element(self,?name):
????return?OxmlElement(name)
def?create_attribute(self,?element,?name,?value):
????element.set(ns.qn(name),?value)
def?add_page_number(self,?run):
????"""
????添加頁面索引
????:param?run:
????:return:
????"""
????fldChar1?=?self.create_element('w:fldChar')
????self.create_attribute(fldChar1,?'w:fldCharType',?'begin')
????instrText?=?self.create_element('w:instrText')
????self.create_attribute(instrText,?'xml:space',?'preserve')
????instrText.text?=?"PAGE"
????fldChar2?=?self.create_element('w:fldChar')
????self.create_attribute(fldChar2,?'w:fldCharType',?'end')
????# run._r:class 'docx.oxml.text.run.CT_R'>
????run._r.append(fldChar1)
????run._r.append(instrText)
????run._r.append(fldChar2)
默認生成的數(shù)字索引在頁腳左下角,并不美觀!
因此,這里我們可以使用?第一篇文章?的方法創(chuàng)建一個「文字塊樣式」,然后以文字塊 Run 的形式,添加到頁腳的第一個段落中去
#?注意:要設(shè)置頁眉頁腳的對齊方式,必須設(shè)置到段落上(文字塊不能添加對齊方式)
doc.sections[0].footer.paragraphs[0].alignment?=?WD_PARAGRAPH_ALIGNMENT.CENTER
#?創(chuàng)建一個文字塊樣式,指定字體名稱、大小、顏色
style?=?create_style(document=doc,?style_name="style",?style_type=2,?font_size=10,
?????????????????????font_color=[0x00,?0x00,?0x00],?font_name="黑體")
self.add_page_number(doc.sections[0].footer.paragraphs[0].add_run("",?style))
doc.save("./output.docx")
print('添加頁碼索引成功!')
需要注意的,如果需要設(shè)置頁面數(shù)字索引的對齊方式,必須針對頁腳的段落進行設(shè)置,修改其 alignment 屬性值即可
5. doc 轉(zhuǎn) docx
python-docx 對?doc 格式的文檔不太友好,要處理這類文檔,我們需要先將它轉(zhuǎn)換為 docx 格式
對于 Windows 系統(tǒng),完全可以使用 win32com 這個模塊,用命令去調(diào)用 Word 應(yīng)用,打開源文件后,保存了 docx 格式的文件即可
from?win32com?import?client
def?doc_to_docx_in_win(path_raw,?path_output):
????"""
????doc轉(zhuǎn)為docx(win)
????:param?path_original:
????:param?path_final:
????:return:
????"""
????#?獲取文件的格式后綴
????file_suffix?=?os.path.splitext(path_raw)[1]
????if?file_suffix?==?".doc":
????????word?=?client.Dispatch('Word.Application')
????????#?源文件
????????doc?=?word.Documents.Open(path_raw)
????????#?生成的新文件
????????doc.SaveAs(path_output,?16)
????????doc.Close()
????????word.Quit()
????elif?file_suffix?==?".docx":
????????shutil.copy(path_raw,?path_output)
而對于 Mac/Linux,推薦使用?LibreOffice 去轉(zhuǎn)換文檔格式
#?轉(zhuǎn)換格式
./soffice?--headless?--convert-to?docx?源文件.doc?--outdir?/output/path/
PS:LibreOffice?是一款由社區(qū)創(chuàng)造的自由免費辦公套件,跨平臺,內(nèi)置的?soffice?可以用于文件轉(zhuǎn)換
以 Mac OS 為例,我們按下面步驟來操作
官網(wǎng)下載?LibreOffice 軟件并安裝
找到?LibreOffice?軟件安裝目錄,將 soffice 命令所在目錄配置到環(huán)境變量中
重啟 Pycharm
使用 os?模塊下的 walk() 函數(shù)遍歷所有源文件,組成一條?soffice 轉(zhuǎn)換命令
執(zhí)行轉(zhuǎn)換命令
import?os
source?=?"./doc/"
dest?=?"./docx/"
g?=?os.walk(source)
#?遍歷文件夾
for?root,?dirs,?files?in?g:
????for?file?in?files:
????????#?源文件完整路徑
????????file_path_raw?=?os.path.join(root,?file)
????????print(file_path_raw)
????????os.system("soffice?--headless?--convert-to?docx?{}?--outdir?{}".format(file_path_raw,?dest))
6. 對比文檔差異性
兩個 Word 文檔的對比也是工作中比較常見的需求了
首先,遍歷文檔中所有段落,過濾掉空行,獲取所有文本內(nèi)容
#?分別獲取段落內(nèi)容
content1?=?''
content2?=?''
for?paragraph?in?file1.paragraphs:
????if?""?==?paragraph.text.strip():
????????continue
????content1?+=?paragraph.text?+?'\n'
for?paragraph?in?file2.paragraphs:
????if?""?==?paragraph.text.strip():
????????continue
????content2?+=?paragraph.text?+?'\n'
#?如果參數(shù) keepends 為 False,不包含換行符,如果為 True,則保留換行符。
print("第二個文檔數(shù)據(jù)如下:\n",?content1.splitlines(keepends=False))
print("第一個文檔數(shù)據(jù)如下:\n",?content1.splitlines(keepends=False))
接著,使用 Python 中的標準依賴庫?difflib?對比文字間的差異,最后生成 HTML 差異報告
import?codecs
from?difflib?import?HtmlDiff
#?差異內(nèi)容
diff_html?=?HtmlDiff(wrapcolumn=100).make_file(content1.split("\n"),?content2.split("\n"))
#?寫入到文件中
with?codecs.open('./diff_result.html',?'w',?encoding='utf-8')?as?f:
?????f.write(diff_html)
7. 特別內(nèi)容標注
我們經(jīng)常需要對文檔中部分重要內(nèi)容進行特別標注
比如,我們需要對文檔中包含「 微信?」的文字塊或單元格,標為紅色并加粗顯示
1 - 段落內(nèi)容
只需要遍歷出段落中所有文字塊 Run,直接修改文字塊的?Font?屬性即可
doc?=?Document(file)
#?關(guān)鍵字的文字塊或單元格標紅,并加粗
#?1、修改段落中包含關(guān)鍵字的文件塊的樣式
for?paragraph?in?doc.paragraphs:
????for?run?in?paragraph.runs:
????????if?keyword?in?run.text:
????????????#?修改顏色為紅色,并加粗顯示
????????????run.font.bold?=?True
????????????run.font.color.rgb?=?RGBColor(255,?0,?0)
2 - 表格內(nèi)容
設(shè)置滿足條件的單元格樣式有點特別,需要經(jīng)過下面 4 個步驟
獲取單元格對象,獲取單元格文本內(nèi)容,并臨時保存
清空單元格數(shù)據(jù)
單元格對象追加一個段落和一個文字塊 Run,返回一個文字塊對象
設(shè)置文字塊對象樣式,標紅并加粗
tables?=?[table?for?table?in?doc.tables]
for?table?in?tables:
????for?row?in?table.rows:
????????for?cell?in?row.cells:
????????????if?keyword?in?cell.text:
????????????????#?原內(nèi)容
????????????????content_raw?=?cell.text
????????????????#?清空單元格數(shù)據(jù)
????????????????cell.text?=?""
????????????????#?追加數(shù)據(jù)進去,并設(shè)置樣式
????????????????run?=?cell.paragraphs[0].add_run(content_raw)
????????????????run.font.color.rgb?=?RGBColor(255,?0,?0)
????????????????run.font.bold?=?True?8. 替換文字內(nèi)容
有時候,我們需要將文檔中某個關(guān)鍵字全部替換成一個新的內(nèi)容
這時候,我們可以遍歷所有段落和表格,使用?replace()?函數(shù)對段落文本和單元格內(nèi)容進行替換
def?replace_content(self,?old_content,?new_content):
????"""
????替換文檔中所有內(nèi)容
????:param?old_content:舊的內(nèi)容
????:param?new_content:新的內(nèi)容
????:return:
????"""
????#?替換段落
????for?paragraph?in?self.doc.paragraphs:
????????if?old_content?in?paragraph.text:
????????????#?替換內(nèi)容后,重新設(shè)置進去
????????????paragraph.text?=?paragraph.text.replace(old_content,?new_content)
????#?替換表格
????# document.tables[表格索引].rows[行索引].cells[單元格列索引].text =?“新的數(shù)據(jù)”。
????tables?=?[table?for?table?in?self.doc.tables]
????for?table?in?tables:
????????for?row?in?table.rows:
????????????for?cell?in?row.cells:
????????????????if?old_content?in?cell.text:
????????????????????#?重新設(shè)置單元格內(nèi)容
????????????????????cell.text?=?cell.text.replace(old_content,?new_content)
????#?保存到一個新的文件中
????self.doc.save('./new.docx')9. 最后
到此,Python 自動化 Word 篇的內(nèi)容全部結(jié)束了!
如果實際工作中,有一些其他的業(yè)務(wù)場景文中沒有覆蓋到,可以在文末進行留言,后面辦公自動化實戰(zhàn)篇可能會提供對應(yīng)的解決方案!
要獲取全部源碼,關(guān)注公眾號,后臺回復(fù)「?word?」即可獲得全部源碼
如果你覺得文章還不錯,請大家?點贊、分享、留言?下,因為這將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強動力!
