最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Word(下)

image

1. 前言

關(guān)于 Word 文檔的讀寫(xiě)婿牍,前面兩篇文章分別進(jìn)行了一次全面的總結(jié)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Word(上)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Word(中)

本篇文章作為一個(gè)辦公自動(dòng)化 Word 篇的一個(gè)補(bǔ)充鳖昌,寫(xiě)寫(xiě)幾個(gè)比較實(shí)用的辦公場(chǎng)景

包含:

  • 頁(yè)眉頁(yè)腳處理

  • 合并多個(gè)文檔

  • 新增數(shù)字索引

  • doc 批量轉(zhuǎn) docx

  • 對(duì)比文檔差異性

  • 特別內(nèi)容標(biāo)注

  • 替換文字內(nèi)容

2. 頁(yè)眉頁(yè)腳

每一個(gè)頁(yè)面章節(jié)都包含:頁(yè)眉頁(yè)腳

它可以單獨(dú)設(shè)置喷好,每個(gè)頁(yè)面都不一樣;也可以全部設(shè)置成與首頁(yè)一樣

這個(gè)功能禀倔,由章節(jié)對(duì)象中的屬性 different_first_page_header_footer 來(lái)控制

  • 當(dāng)值為 True 時(shí)参淫,代表頁(yè)眉頁(yè)腳不同于首頁(yè),每個(gè)頁(yè)面章節(jié)的頁(yè)眉涎才、頁(yè)腳都可以單獨(dú)設(shè)置

  • 當(dāng)值為 False 時(shí)鞋既,所有頁(yè)面的頁(yè)眉耍铜、頁(yè)腳都一樣

# 1、獲取待處理頁(yè)眉棕兼、頁(yè)腳的章節(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:頁(yè)眉頁(yè)腳不同于首頁(yè)陡舅,每個(gè)頁(yè)面章節(jié)的頁(yè)眉頁(yè)腳單獨(dú)設(shè)置
# False:每個(gè)頁(yè)面的頁(yè)眉頁(yè)腳相同
self.doc.sections[0].different_first_page_header_footer = True

添加頁(yè)眉頁(yè)腳包含兩種,分別是:普通頁(yè)眉頁(yè)腳伴挚、自定義樣式的頁(yè)眉頁(yè)腳

1 - 普通頁(yè)眉頁(yè)腳

def add_norm_header_and_footer(header, footer, header_content, footer_content):
    """
    增加一個(gè)普通的頁(yè)眉章鲤、頁(yè)腳咆贬,并居中顯示
    :param header_content:
    :param footer_content:
    :return:
    """
    # 新增/修改頁(yè)眉、頁(yè)腳
    # 注意:一般頁(yè)眉皱蹦、頁(yè)腳里只有一個(gè)段落
    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眷蜈、新增頁(yè)眉
# 2.1 普通的頁(yè)眉、頁(yè)腳
add_norm_header_and_footer(header, footer, "我是一個(gè)頁(yè)眉", "我是一個(gè)頁(yè)腳")
2 - 自帶樣式的頁(yè)眉頁(yè)腳

2 - 自帶樣式的頁(yè)眉頁(yè)腳

def add_custom_style_header_and_footer(header, footer, header_content, footer_content, style):
    """
    新增自定義的頁(yè)眉辜妓、頁(yè)腳
    :param header:
    :param footer:
    :param header_content:
    :param footer_content:
    :param style:
    :return:
    """
    # 注意:style_type=2籍滴,否則會(huì)報(bào)錯(cuò)
    header.paragraphs[0].add_run(header_content, style)
    footer.paragraphs[0].add_run(footer_content, style)

# 2.2 自帶樣式的頁(yè)眉榴啸、頁(yè)腳
# 創(chuàng)建一個(gè)樣式
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, "我是頁(yè)眉2", "我是頁(yè)腳2", style_paragraph)

如果想將文檔中所有的頁(yè)眉鸥印、頁(yè)腳刪除掉坦报,只需要 2 個(gè)步驟:

  • 遍歷文檔中所有頁(yè)面章節(jié)狂鞋,將其 different_first_page_header_footer 屬性值設(shè)置為 False

  • 設(shè)置章節(jié)對(duì)象頁(yè)眉頁(yè)腳的 is_linked_to_previous 屬性值為 True

    PS:當(dāng) is_linked_to_previous 設(shè)置為 True 時(shí),頁(yè)眉頁(yè)腳會(huì)被刪除

def remove_all_header_and_footer(doc):
    """
    刪除文檔中所有頁(yè)眉和頁(yè)腳
    :param doc:
    :return:
    """
    for section in doc.sections:
        section.different_first_page_header_footer = False
        # 當(dāng)is_linked_to_previous設(shè)置為T(mén)rue時(shí)骚揍,頁(yè)眉頁(yè)腳會(huì)被刪除
        section.header.is_linked_to_previous = True
        section.footer.is_linked_to_previous = True

3. 合并多個(gè)文檔

日常工作中疏咐,經(jīng)常會(huì)遇到將多個(gè) Word 文檔合并成一個(gè)文件的需求

這里,可以使用另外一個(gè) Python 依賴庫(kù):docxcompose

# 合并多個(gè)文件的依賴庫(kù)
pip3 install docxcompose

使用也非常簡(jiǎn)單借跪,只需要下面 4 行代碼酌壕,就能將多個(gè)文件進(jìn)行合并卵牍,生成到一個(gè)新的文件中去

from docxcompose.composer import Composer

def compose_files(self, files, output_file_path):
    """
    合并多個(gè)word文件到一個(gè)文件中
    :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)常需要在文檔頁(yè)腳處添加頁(yè)面數(shù)字索引,可惜 python-docx 并沒(méi)有提供現(xiàn)有方法

但是辛掠,在 stackoverflow 上找到實(shí)現(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):
    """
    添加頁(yè)面索引
    :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)

默認(rèn)生成的數(shù)字索引在頁(yè)腳左下角萝衩,并不美觀猩谊!

因此祭刚,這里我們可以使用 第一篇文章 的方法創(chuàng)建一個(gè)「文字塊樣式」,然后以文字塊 Run 的形式暗甥,添加到頁(yè)腳的第一個(gè)段落中去

# 注意:要設(shè)置頁(yè)眉頁(yè)腳的對(duì)齊方式遮怜,必須設(shè)置到段落上(文字塊不能添加對(duì)齊方式)
doc.sections[0].footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

# 創(chuàng)建一個(gè)文字塊樣式锯梁,指定字體名稱焰情、大小内舟、顏色
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('添加頁(yè)碼索引成功初橘!')

需要注意的,如果需要設(shè)置頁(yè)面數(shù)字索引的對(duì)齊方式耕蝉,必須針對(duì)頁(yè)腳的段落進(jìn)行設(shè)置夜只,修改其 alignment 屬性值即可

5. doc 轉(zhuǎn) docx

python-docx 對(duì) doc 格式的文檔不太友好扔亥,要處理這類文檔,我們需要先將它轉(zhuǎn)換為 docx 格式

對(duì)于 Windows 系統(tǒng)踢关,完全可以使用 win32com 這個(gè)模塊粘茄,用命令去調(diào)用 Word 應(yīng)用驹闰,打開(kāi)源文件后撒会,保存了 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)

而對(duì)于 Mac/Linux诵肛,推薦使用 LibreOffice 去轉(zhuǎn)換文檔格式

# 轉(zhuǎn)換格式
./soffice --headless --convert-to docx 源文件.doc --outdir /output/path/

PS:LibreOffice 是一款由社區(qū)創(chuàng)造的自由免費(fèi)辦公套件,跨平臺(tái)褪秀,內(nèi)置的 soffice 可以用于文件轉(zhuǎn)換

以 Mac OS 為例薛训,我們按下面步驟來(lái)操作

  • 官網(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. 對(duì)比文檔差異性

兩個(gè) Word 文檔的對(duì)比也是工作中比較常見(jiàn)的需求了

首先出吹,遍歷文檔中所有段落辙喂,過(guò)濾掉空行巍耗,獲取所有文本內(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("第二個(gè)文檔數(shù)據(jù)如下:\n", content1.splitlines(keepends=False))
print("第一個(gè)文檔數(shù)據(jù)如下:\n", content1.splitlines(keepends=False))

接著,使用 Python 中的標(biāo)準(zhǔn)依賴庫(kù) difflib 對(duì)比文字間的差異杏慰,最后生成 HTML 差異報(bào)告

import codecs
from difflib import HtmlDiff

# 差異內(nèi)容
diff_html = HtmlDiff(wrapcolumn=100).make_file(content1.split("\n"), content2.split("\n"))

# 寫(xiě)入到文件中
with codecs.open('./diff_result.html', 'w', encoding='utf-8') as f:
     f.write(diff_html)

7. 特別內(nèi)容標(biāo)注

我們經(jīng)常需要對(duì)文檔中部分重要內(nèi)容進(jìn)行特別標(biāo)注

比如炼鞠,我們需要對(duì)文檔中包含「 微信 」的文字塊或單元格谒主,標(biāo)為紅色并加粗顯示

1 - 段落內(nèi)容

只需要遍歷出段落中所有文字塊 Run,直接修改文字塊的 Font 屬性即可

doc = Document(file)

# 關(guān)鍵字的文字塊或單元格標(biāo)紅擎颖,并加粗
# 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è)置滿足條件的單元格樣式有點(diǎn)特別,需要經(jīng)過(guò)下面 4 個(gè)步驟

  • 獲取單元格對(duì)象聋丝,獲取單元格文本內(nèi)容潮针,并臨時(shí)保存

  • 清空單元格數(shù)據(jù)

  • 單元格對(duì)象追加一個(gè)段落和一個(gè)文字塊 Run,返回一個(gè)文字塊對(duì)象

  • 設(shè)置文字塊對(duì)象樣式瓣戚,標(biāo)紅并加粗

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ù)進(jìn)去焦读,并設(shè)置樣式
                run = cell.paragraphs[0].add_run(content_raw)
                run.font.color.rgb = RGBColor(255, 0, 0)
                run.font.bold = True 

8. 替換文字內(nèi)容

有時(shí)候矗晃,我們需要將文檔中某個(gè)關(guān)鍵字全部替換成一個(gè)新的內(nèi)容

這時(shí)候,我們可以遍歷所有段落和表格仓技,使用 replace() 函數(shù)對(duì)段落文本和單元格內(nèi)容進(jìn)行替換

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è)置進(jìn)去
            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)

    # 保存到一個(gè)新的文件中
    self.doc.save('./new.docx')

9. 最后

到此羡亩,Python 自動(dòng)化 Word 篇的內(nèi)容全部結(jié)束了!

如果實(shí)際工作中雷袋,有一些其他的業(yè)務(wù)場(chǎng)景文中沒(méi)有覆蓋到及志,可以在文末進(jìn)行留言速侈,后面辦公自動(dòng)化實(shí)戰(zhàn)篇可能會(huì)提供對(duì)應(yīng)的解決方案迫卢!

要獲取全部源碼乾蛤,關(guān)注公眾號(hào)「 AirPython 」捅僵,后臺(tái)回復(fù)「 word 」即可獲得全部源碼

如果你覺(jué)得文章還不錯(cuò)眨层,請(qǐng)大家 點(diǎn)贊趴樱、分享、留言下纳账,因?yàn)檫@將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強(qiáng)動(dòng)力疏虫!

推薦閱讀
最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Excel(上)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Excel(中)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Excel(下)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Word(上)

最全總結(jié) | 聊聊 Python 辦公自動(dòng)化之 Word(中)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卧秘,一起剝皮案震驚了整個(gè)濱河市官扣,隨后出現(xiàn)的幾起案子醇锚,更是在濱河造成了極大的恐慌,老刑警劉巖恋昼,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件液肌,死亡現(xiàn)場(chǎng)離奇詭異鸥滨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)老速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)橘券,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旁舰,“玉大人,你說(shuō)我怎么就攤上這事毯焕』怯#” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)活孩。 經(jīng)常有香客問(wèn)我憾儒,道長(zhǎng),這世上最難降的妖魔是什么诗舰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任眶根,我火速辦了婚禮边琉,結(jié)果婚禮上变姨,老公的妹妹穿的比我還像新娘。我一直安慰自己渔呵,他們只是感情好砍鸠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布睦番。 她就那樣靜靜地躺著托嚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兢哭。 梳的紋絲不亂的頭發(fā)上夫嗓,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天舍咖,我揣著相機(jī)與錄音排霉,去河邊找鬼。 笑死球订,一個(gè)胖子當(dāng)著我的面吹牛瑰钮,可吹牛的內(nèi)容都是我干的浪谴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼梁呈!你這毒婦竟也來(lái)了官卡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哮翘,失蹤者是張志新(化名)和其女友劉穎饭寺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體限煞,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡署驻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年旺上,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宣吱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞳别。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洒试,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卒煞,到底是詐尸還是另有隱情叼架,我是刑警寧澤乖订,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布乍构,位于F島的核電站,受9級(jí)特大地震影響岂丘,放射性物質(zhì)發(fā)生泄漏眠饮。R本人自食惡果不足惜仪召,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望已旧。 院中可真熱鬧秸苗,春花似錦、人聲如沸评姨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吐句。三九已至,卻和暖如春店读,著一層夾襖步出監(jiān)牢的瞬間嗦枢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工屯断, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人殖演。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓氧秘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親趴久。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丸相,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容