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

1. 前言
上一篇文章,對 Word 寫入數(shù)據(jù)的一些常見操作進行了總結(jié)
最全總結(jié) | 聊聊 Python 辦公自動化之 Word(上)
相比寫入數(shù)據(jù),讀取數(shù)據(jù)同樣很實用!
本篇文章,將談?wù)勅绾稳孀x取一個 Word 文檔中的數(shù)據(jù),并會指出一些要注意的點
2. 基本信息
我們同樣使用 python-docx 這個依賴庫來對?Word 文檔進行讀取
首先我們來讀取文檔的基本信息
它們分別是:章節(jié)、頁邊距、頁眉頁腳邊距、頁面寬高、頁面方向等
在獲取文檔基礎(chǔ)信息之前,我們通過文檔路徑構(gòu)建一個文檔對象 Document
from?docx?import?Document
#?源文件目錄
self.word_path?=?'./output.docx'
#?打開文檔,構(gòu)建一個文檔對象
self.doc?=?Document(self.word_path)
1 - 章節(jié)( Section )
#?1、獲取章節(jié)信息
#?注意:章節(jié)可以設(shè)置本頁的大小、頁眉、頁腳
msg_sections?=?self.doc.sections
print("章節(jié)列表:",?msg_sections)
#?章節(jié)數(shù)目
print('章節(jié)數(shù)目:',?len(msg_sections))
2 -?頁邊距( Page Margin )
通過章節(jié)對象的?left_margin、top_margin、right_margin、bottom_margin 屬性值可以獲取當(dāng)前章節(jié)的左邊距、上邊距、右邊距、下邊距
def?get_page_margin(section):
????"""
????獲取某個頁面的頁邊距(EMU)
????:param?section:
????:return:
????"""
????#?分別對應(yīng):左邊距、上邊距、右邊距、下邊距
????left,?top,?right,?bottom?=?section.left_margin,?section.top_margin,?section.right_margin,?section.bottom_margin
????return?left,?top,?right,?bottom
#?2、頁邊距信息
first_section?=?msg_sections[0]
left,?top,?right,?bottom?=?get_page_margin(first_section)
print('左邊距:',?left,?",上邊距:",?top,?",右邊距:",?right,?",下邊距:",?bottom)返回值的單位是 EMU,和厘米、英尺的轉(zhuǎn)換關(guān)系如下:

3?-?頁眉頁腳邊距
頁眉邊距:header_distance
頁腳邊距:footer_distance
def?get_header_footer_distance(section):
????"""
????獲取頁眉、頁腳邊距
????:param?section:
????:return:
????"""
????#?分別對應(yīng)頁眉邊距、頁腳邊距
????header_distance,?footer_distance?=?section.header_distance,?section.footer_distance
????return?header_distance,?footer_distance
#?3、頁眉頁腳邊距
header_distance,?footer_distance?=?get_header_footer_distance(first_section)
print('頁眉邊距:',?header_distance,?",頁腳邊距:",?footer_distance)
4 - 頁面寬度和高度
頁面寬度:page_width
頁面高度:page_height
def?get_page_size(section):
????"""
????獲取頁面寬度、高度
????:param?section:
????:return:
????"""
????#?分別對應(yīng)頁面寬度、高度
????page_width,?page_height?=?section.page_width,?section.page_height
????return?page_width,?page_height
#?4、頁面寬度、高度
page_width,?page_height?=?get_page_size(first_section)
print('頁面寬度:',?page_width,?",頁面高度:",?page_height)
5 - 頁面方向( Page Orientation )
頁面方向分為:橫向和縱向
使用章節(jié)對象的?orientation 屬性去獲取一個章節(jié)的頁面方向
def?get_page_orientation(section):
????"""
????獲取頁面方向
????:param?section:
????:return:
????"""
????return?section.orientation
#?5、頁面方向
#?類型:class 'docx.enum.base.EnumValue
#?包含:PORTRAIT (0)、LANDSCAPE (1)
page_orientation?=?get_page_orientation(first_section)
print("頁面方向:",?page_orientation)
同樣,可以直接使用這個屬性設(shè)置一個章節(jié)的方向
from?docx.enum.section?import?WD_ORIENT
#?設(shè)置頁面方向(橫向、豎向)
#?設(shè)置為橫向
first_section.orientation?=?WD_ORIENT.LANDSCAPE
#?設(shè)置為豎向
#?first_section.orientation?=?WD_ORIENT.PORTRAIT
self.doc.save(self.word_path)3. 段落
使用文檔對象的?paragraphs 屬性可以獲取文檔中所有的段落
注意:這里獲取的段落不包含頁眉、頁腳、表格中的段落
#?獲取文檔對象中所有的段落,默認不包含:頁眉、頁腳、表格中的段落
paragraphs?=?self.doc.paragraphs
#?1、段落數(shù)目
paragraphs_length?=?len(paragraphs)
print('文檔中一共包含:{}個段落'.format(paragraphs_length))
1 - 段落內(nèi)容
我們可以遍歷文檔中所有的段落列表,通過段落對象的 text 屬性,獲取全部的段落內(nèi)容
#?0、讀取所有段落數(shù)據(jù)
contents?=?[paragraph.text?for?paragraph?in?self.doc.paragraphs]
print(contents)
2 - 段落格式
通過上一篇文章,我們知道段落也存在格式的
使用?paragraph_format 屬性獲取段落的基本格式信息
包含:對齊方式、左右縮進、行間距、段落前后間距等
#?2、獲取某一個段落的格式信息
paragraph_someone?=?paragraphs[0]
#?2.1?段落內(nèi)容
content?=?paragraph_someone.text
print('段落內(nèi)容:',?content)
#?2.2?段落格式
paragraph_format?=?paragraph_someone.paragraph_format
#?2.2.1?對齊方式
#?
alignment?=?paragraph_format.alignment
print('段落對齊方式:',?alignment)
#?2.2.2?左、右縮進
left_indent,?right_indent?=?paragraph_format.left_indent,?paragraph_format.right_indent
print('段落左縮進:',?left_indent,?",右縮進:",?right_indent)
#?2.2.3?首行縮進
first_line_indent?=?paragraph_format.first_line_indent
print('段落首行縮進:',?first_line_indent)
#?2.2.4?行間距
line_spacing?=?paragraph_format.line_spacing
print('段落行間距:',?line_spacing)
#?2.2.5?段落前后間距
space_before,?space_after?=?paragraph_format.space_before,?paragraph_format.space_after
print('段落前、后間距分別為:',?space_before,?',',?space_after)
4. 文字塊 - Run
文字塊 Run 屬于段落的一部分,所以,要獲取文字塊信息,必須先拿到一個段落實例對象
以文字塊基本信息、字體格式信息為例
1 - 文字塊基本信息
我們使用段落對象的 runs 屬性獲取段落內(nèi)所有的文字塊對象
def?get_runs(paragraph):
????"""
????獲取段落下所有的文字塊信息,包含:數(shù)目、內(nèi)容列表
????:param?paragraph:
????:return:
????"""
????#?段落對象包含的文字塊Run
????runs?=?paragraph.runs
????#?數(shù)量
????runs_length?=?len(runs)
????#?文字塊內(nèi)容
????runs_contents?=?[run.text?for?run?in?runs]
????return?runs,?runs_length,?runs_contents
2 - 文字塊格式信息
文字塊是文檔中最小的文字單元,使用文字塊對象的 font 屬性可以拿到它的字體屬性
和設(shè)置文字塊格式屬性一一對應(yīng),字體名稱、大小、顏色、是否加粗、是否斜體等都可以獲取到
#?2、文字塊格式信息
#?包含:字體名稱、大小、顏色、是否加粗等
#?某一個文字塊的字體屬性
run_someone_font?=?runs[0].font
#?字體名稱
font_name?=?run_someone_font.name
print('字體名稱:',?font_name)
#?字體顏色(RGB)
#?
font_color?=?run_someone_font.color.rgb
print('字體顏色:',?font_color)
print(type(font_color))
#?字體大小
font_size?=?run_someone_font.size
print('字體大小:',?font_size)
#?是否加粗
# True:加粗;None/False:沒有加粗
font_bold?=?run_someone_font.bold
print('是否加粗:',?font_bold)
#?是否斜體
# True:協(xié)議;None/False:不是斜體
font_italic?=?run_someone_font.italic
print('是否斜體:',?font_italic)
#?帶下劃線
# True:帶有下滑線;None/False:字體沒有帶下滑線
font_underline?=?run_someone_font.underline
print('帶有下滑線:',?font_underline)
#?刪除線/雙刪除線
# True:帶有刪除線;None/False:字體沒有帶刪除線
font_strike?=?run_someone_font.strike
font_double_strike?=?run_someone_font.double_strike
print('帶有刪除線:',?font_strike,?"\n帶有雙刪除線:",?font_double_strike)5. 表格
文檔對象的?tables 屬性可以獲取當(dāng)前文檔中所有的表格對象
#?文檔中所有的表格對象
tables?=?self.doc.tables
#?1、表格數(shù)量
table_num?=?len(tables)
print('文檔中包含的表格數(shù)量:',?table_num)
1 - 表格所有數(shù)據(jù)
獲取表格中所有數(shù)據(jù)有 2 種方式
第一種方式:通過遍歷文檔中所有表格,然后按行和單元格進行遍歷,最后通過單元格的 text 屬性獲取所有單元格的文本內(nèi)容
#?2、讀取所有表格數(shù)據(jù)
#?所有表格對象
#?tables?=?[table?for?table?in?self.doc.tables]
print('內(nèi)容分別是:')
for?table?in?tables:
????for?row?in?table.rows:
????????for?cell?in?row.cells:
????????????print(cell.text,?end='??')
????????print()
????print('\n')
另外一種方式是使用表格對象的?_cells 屬性獲取表格中所有的單元格,然后遍歷獲取單元格的值
def?get_table_cell_content(table):
????"""
????讀取表格中所有單元格是內(nèi)容
????:param?table:
????:return:
????"""
????#?所有單元格
????cells?=?table._cells
????cell_size?=?len(cells)
????#?所有單元格的內(nèi)容
????content?=?[cell.text?for?cell?in?cells]
????return?content2 - 表格樣式
#?3、表格樣式名稱
#?Table?Grid
table_someone?=?tables[0]
style?=?table_someone.style.name
print("表格樣式:",?style)
3 - 表格行數(shù)量、列數(shù)量
table.rows:表格中的行數(shù)據(jù)迭代對象
table.columns:表格中的列數(shù)據(jù)迭代對象def?get_table_size(table):
????"""
????獲取表格的行數(shù)量、列數(shù)量
????:param?table:
????:return:
????"""
????#?幾行、幾列
????row_length,?column_length?=?len(table.rows),?len(table.columns)
????return?row_length,?column_length
4 - 行數(shù)據(jù)、列數(shù)據(jù)
有時候,我們需要單獨按照行或者列,獲取全部數(shù)據(jù)def?get_table_row_datas(table):
????"""
????獲取表格中行數(shù)據(jù)
????:param?table:
????:return:
????"""
????rows?=?table.rows
????datas?=?[]
????#?每一行獲取單元格的數(shù)據(jù)組成列表,加入到結(jié)果列表中
????for?row?in?rows:
????????datas.append([cell.text?for?cell?in?row.cells])
????return?datas
def?get_table_column_datas(table):
????"""
????獲取表格中列數(shù)據(jù)
????:param?table:
????:return:
????"""
????columns?=?table.columns
????datas?=?[]
????#?每一列獲取單元格的數(shù)據(jù)組成列表,加入到結(jié)果列表中
????for?column?in?columns:
????????datas.append([cell.text?for?cell?in?column.cells])
????return?datas6. 圖片
有時候,我們需要將 Word 文檔中的圖片下載到本地
Word 文檔實際上也是一個壓縮文件,我們使用解壓工具后發(fā)現(xiàn),文檔包含的圖片都放置在 /word/media/ 目錄下

提取文檔圖片有 2 種方法,分別是:
解壓文檔文件,將對應(yīng)目錄下的圖片拷貝出來
使用?python-docx 內(nèi)置的方法提取圖片( 推薦 )
def?get_word_pics(doc,?word_path,?output_path):
????"""
????提取word文檔內(nèi)的圖片
????:param?word_path:源文件名稱
????:param?output_path:?結(jié)果目錄
????:return:
????"""
????dict_rel?=?doc.part._rels
????for?rel?in?dict_rel:
????????rel?=?dict_rel[rel]
????????if?"image"?in?rel.target_ref:
????????????#?圖片保存目錄
????????????if?not?os.path.exists(output_path):
????????????????os.makedirs(output_path)
????????????img_name?=?re.findall("/(.*)",?rel.target_ref)[0]
????????????word_name?=?os.path.splitext(word_path)[0]
????????????#?新的名稱
????????????newname?=?word_name.split('\\')[-1]?if?os.sep?in?word_name?else?word_name.split('/')[-1]
????????????img_name?=?f'{newname}_{img_name}'
????????????#?寫入到文件中
????????????with?open(f'{output_path}/{img_name}',?"wb")?as?f:
????????????????f.write(rel.target_part.blob)
7. 頁眉頁腳
頁眉和頁腳都是基于章節(jié)
我們以某一個章節(jié)對象為例進行說明
#?獲取某一個章節(jié)
first_section?=?self.doc.sections[0]使用章節(jié)對象的?header、footer 屬性可以獲取頁眉、頁腳對象
由于頁眉、頁腳可能包含多個段落 Paragraph,因此,我們可以先使用頁眉頁腳對象的?paragraphs 屬性獲取所有段落,然后遍歷出所有段落的值,最后拼接起來就是頁眉頁腳的全部內(nèi)容
#?注意:頁眉、頁腳都有可能包含多個段落
#?頁眉所有的段落
header_content?=?"?".join([paragraph.text?for?paragraph?in?first_section.header.paragraphs])
print("頁眉內(nèi)容:",?header_content)
#?頁腳
footer_content?=?"?".join([paragraph.text?for?paragraph?in?first_section.footer.paragraphs])
print("頁腳內(nèi)容:",?footer_content)8. 最后
本篇文章和上一篇文章,分別對 Word 文檔的讀寫進行了一次全面講解
文中全部源碼我已經(jīng)上傳到后臺,關(guān)注公眾號,回復(fù)「?word?」即可獲得全部源碼
如果你覺得文章還不錯,請大家?點贊、分享、留言?下,因為這將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強動力!
