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

image

1. 前言

關於 Word 文檔的讀寫,前面兩篇文章分別進行了一次全面的總結

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

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

本篇文章作為一個辦公自動化 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 上找到實現的方式

//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)

默認生成的數字索引在頁腳左下角,並不美觀!

因此,這裡我們可以使用 第一篇文章 的方法創建一個「文字塊樣式」,然後以文字塊 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(上)

最全總結 | 聊聊 Python 辦公自動化之 Excel(中)

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

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

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