最全總結 | 聊聊 Python 辦公自動化之 Word(下)
阿新 • • 發佈:2020-11-25
![image](https://upload-images.jianshu.io/upload_images/1466987-90bf00bce61186d0?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
## 1\. 前言
關於 Word 文件的讀寫,前面兩篇文章分別進行了一次全面的總結
[最全總結 | 聊聊 Python 辦公自動化之 Word(上)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486899&idx=1&sn=d25d979b7cab442a5a062ded7f4cf31a&chksm=fc1b7373cb6cfa6544c802baab9c46aeeb5d0d26830c61ca9c3843bdc57927d0df5da868ae55&scene=21#wechat_redirect)
[最全總結 | 聊聊 Python 辦公自動化之 Word(中)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486920&idx=1&sn=eae4316628ea3c6e93d4808d5bafb5c2&chksm=fc1b7308cb6cfa1ef14cd0d345ae81d8e31659d72bc77a7ebee1237ceb6f36f97457cc6ef2c1&scene=21#wechat_redirect)
本篇文章作為一個辦公自動化 Word 篇的一個補充,寫寫幾個比較實用的辦公場景
包含:
* 頁首頁尾處理
* 合併多個文件
* 新增數字索引
* doc 批量轉 docx
* 對比文件差異性
* 特別內容標註
* 替換文字內容
## 2\. 頁首頁尾
每一個頁面章節都包含:頁首頁尾
它可以單獨設定,每個頁面都不一樣;也可以全部設定成與首頁一樣
這個功能,由章節物件中的屬性 different_first_page_header_footer 來控制
* 當值為 True 時,代表頁首頁尾不同於首頁,每個頁面章節的頁首、頁尾都可以單獨設定
* 當值為 False 時,所有頁面的頁首、頁尾都一樣
```
# 1、獲取待處理頁首、頁尾的章節
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:頁首頁尾不同於首頁,每個頁面章節的頁首頁尾單獨設定
# 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 - 自帶樣式的頁首頁尾
```
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 自帶樣式的頁首、頁尾
# 建立一個樣式
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 個步驟:
* 遍歷文件中所有頁面章節,將其 different_first_page_header_footer 屬性值設定為 False
* 設定章節物件頁首頁尾的 is_linked_to_previous 屬性值為 True
PS:當 is_linked_to_previous 設定為 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設定為True時,頁首頁尾會被刪除
section.header.is_linked_to_previous = True
section.footer.is_linked_to_previous = True
```
## 3\. 合併多個文件
日常工作中,經常會遇到將多個 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\. 新增數字索引
我們經常需要在文件頁尾處新增頁面數字索引,可惜 python-docx 並沒有提供現有方法
但是,在 stackoverflow 上找到實現的方式
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)
```
預設生成的數字索引在頁尾左下角,並不美觀!
因此,這裡我們可以使用 [第一篇文章](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486899&idx=1&sn=d25d979b7cab442a5a062ded7f4cf31a&chksm=fc1b7373cb6cfa6544c802baab9c46aeeb5d0d26830c61ca9c3843bdc57927d0df5da868ae55&scene=21#wechat_redirect) 的方法建立一個「文字塊樣式」,然後以文字塊 Run 的形式,新增到頁尾的第一個段落中去
```
# 注意:要設定頁首頁尾的對齊方式,必須設定到段落上(文字塊不能新增對齊方式)
doc.sections[0].footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
# 建立一個文字塊樣式,指定字型名稱、大小、顏色
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('新增頁碼索引成功!')
```
需要注意的,如果需要設定頁面數字索引的對齊方式,必須針對頁尾的段落進行設定,修改其 alignment 屬性值即可
## 5\. doc 轉 docx
python-docx 對 doc 格式的文件不太友好,要處理這類文件,我們需要先將它轉換為 docx 格式
對於 Windows 系統,完全可以使用 win32com 這個模組,用命令去呼叫 Word 應用,開啟原始檔後,儲存了 docx 格式的檔案即可
```
from win32com import client
def doc_to_docx_in_win(path_raw, path_output):
"""
doc轉為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 去轉換文件格式
```
# 轉換格式
./soffice --headless --convert-to docx 原始檔.doc --outdir /output/path/
```
PS:LibreOffice 是一款由社群創造的自由免費辦公套件,跨平臺,內建的 soffice 可以用於檔案轉換
以 Mac OS 為例,我們按下面步驟來操作
* 官網下載 LibreOffice 軟體並安裝
* 找到 LibreOffice 軟體安裝目錄,將 soffice 命令所在目錄配置到環境變數中
* 重啟 Pycharm
* 使用 os 模組下的 walk() 函式遍歷所有原始檔,組成一條 soffice 轉換命令
* 執行轉換命令
```
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 文件的對比也是工作中比較常見的需求了
首先,遍歷文件中所有段落,過濾掉空行,獲取所有文字內容
```
# 分別獲取段落內容
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'
# 如果引數 keepends 為 False,不包含換行符,如果為 True,則保留換行符。
print("第二個文件資料如下:\n", content1.splitlines(keepends=False))
print("第一個文件資料如下:\n", content1.splitlines(keepends=False))
```
接著,使用 Python 中的標準依賴庫 difflib 對比文字間的差異,最後生成 HTML 差異報告
```
import codecs
from difflib import HtmlDiff
# 差異內容
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\. 特別內容標註
我們經常需要對文件中部分重要內容進行特別標註
比如,我們需要對文件中包含「 微信 」的文字塊或單元格,標為紅色並加粗顯示
1 - 段落內容
只需要遍歷出段落中所有文字塊 Run,直接修改文字塊的 Font 屬性即可
```
doc = Document(file)
# 關鍵字的文字塊或單元格標紅,並加粗
# 1、修改段落中包含關鍵字的檔案塊的樣式
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 - 表格內容
設定滿足條件的單元格樣式有點特別,需要經過下面 4 個步驟
* 獲取單元格物件,獲取單元格文字內容,並臨時儲存
* 清空單元格資料
* 單元格物件追加一個段落和一個文字塊 Run,返回一個文字塊物件
* 設定文字塊物件樣式,標紅並加粗
```
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:
# 原內容
content_raw = cell.text
# 清空單元格資料
cell.text = ""
# 追加資料進去,並設定樣式
run = cell.paragraphs[0].add_run(content_raw)
run.font.color.rgb = RGBColor(255, 0, 0)
run.font.bold = True
```
## 8\. 替換文字內容
有時候,我們需要將文件中某個關鍵字全部替換成一個新的內容
這時候,我們可以遍歷所有段落和表格,使用 replace() 函式對段落文字和單元格內容進行替換
```
def replace_content(self, old_content, new_content):
"""
替換文件中所有內容
:param old_content:舊的內容
:param new_content:新的內容
:return:
"""
# 替換段落
for paragraph in self.doc.paragraphs:
if old_content in paragraph.text:
# 替換內容後,重新設定進去
paragraph.text = paragraph.text.replace(old_content, new_content)
# 替換表格
# document.tables[表格索引].rows[行索引].cells[單元格列索引].text = “新的資料”。
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:
# 重新設定單元格內容
cell.text = cell.text.replace(old_content, new_content)
# 儲存到一個新的檔案中
self.doc.save('./new.docx')
```
## 9\. 最後
到此,Python 自動化 Word 篇的內容全部結束了!
如果實際工作中,有一些其他的業務場景文中沒有覆蓋到,可以在文末進行留言,後面辦公自動化實戰篇可能會提供對應的解決方案!
要獲取全部原始碼,關注公眾號「 **AirPython** 」,後臺回覆「 **word** 」即可獲得全部原始碼
如果你覺得文章還不錯,請大家 **點贊、分享、留言**下,因為這將是我持續輸出更多優質文章的最強動力!
**推薦閱讀**
[最全總結 | 聊聊 Python 辦公自動化之 Excel(上)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486752&idx=1&sn=bc58a3127bad72f2210817c3b4087801&chksm=fc1b73e0cb6cfaf6b1dbe8943d13a805d384ae74f043720a1d048c498405cc1ada93437f58ce&scene=21#wechat_redirect)
[最全總結 | 聊聊 Python 辦公自動化之 Excel(中)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486789&idx=1&sn=e565569c17815a897ab6089f9c305d90&chksm=fc1b7385cb6cfa93c17632269677f60439e1738b23c7522eb75a5e8dedc61a5c44e0c681c447&scene=21#wechat_redirect)
[最全總結 | 聊聊 Python 辦公自動化之 Excel(下)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486844&idx=1&sn=de68f12d72e3e16289a2ffb0ed578e6d&chksm=fc1b73bccb6cfaaabf05f8548f49a9a6b83debd0997a11a53313b433730bd23189fa8c640e32&scene=21#wechat_redirect)
[最全總結 | 聊聊 Python 辦公自動化之 Word(上)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486899&idx=1&sn=d25d979b7cab442a5a062ded7f4cf31a&chksm=fc1b7373cb6cfa6544c802baab9c46aeeb5d0d26830c61ca9c3843bdc57927d0df5da868ae55&scene=21#wechat_redirect)
[最全總結 | 聊聊 Python 辦公自動化之 Word(中)](http://mp.weixin.qq.com/s?__biz=MzU1OTI0NjI1NQ==&mid=2247486920&idx=1&sn=eae4316628ea3c6e93d4808d5bafb5c2&chksm=fc1b7308cb6cfa1ef14cd0d345ae81d8e31659d72bc77a7ebee1237ceb6f36f97457cc6ef2c1&scene=21#wechat_re