1. 程式人生 > >最全總結 | 聊聊 Python 辦公自動化之 Word(下)

最全總結 | 聊聊 Python 辦公自動化之 Word(下)

![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