工作中正好接到個(gè)小項(xiàng)目需要將Excel數(shù)據(jù)導(dǎo)出組成word表格并合并所有表格成一個(gè)word文檔肪康,這個(gè)流程涉及到Excel的解析荚恶、Word文檔的數(shù)據(jù)插入,多個(gè)Word文檔的合并磷支,對(duì)Word文檔插入圖片等谒撼,在嘗試構(gòu)寫腳本時(shí)發(fā)現(xiàn)百度谷歌的資料多有缺失或者干脆就是過時(shí)的方法,于是寫下這篇文章將自己在構(gòu)寫這個(gè)腳本時(shí)遇到的坑寫一下雾狈。
首先是解析Excel數(shù)據(jù)廓潜,Python的xlrd框架可以快速地幫助我們完成這一部分操作:
import xlrd
//獲取Excel文檔
workbook = xlrd.open_workbook(u'2019名單.xls')
//我們可以通過兩種不同的方式獲取Excel文檔中對(duì)應(yīng)的表單
sheet = workbook.sheets()[0]
sheet = getSheetByName("表單名字",workbook)
//通過xlrd的接口獲得表單的行數(shù)及列數(shù)
nrow = sheet.nrows
ncol = sheet.ncols
for i in range(1,nrow):
//cells代表的是Excel表單的每一行,即一個(gè)單元格組
cells = sheet.row(i)
//得到單元格組我們便可執(zhí)行下一步的插入Word文檔操作了
insertCellsToWord(cells)
需要明白的是箍邮,最好不要在Python腳本中對(duì)Word文檔的效果進(jìn)行再定義茉帅,原因之一是因?yàn)槟_本并不全能,如果在腳本中對(duì)文檔效果進(jìn)行再定義锭弊,代碼會(huì)變得臃腫且難以維護(hù)堪澎,例如先通過腳本生成表單效果再對(duì)單元格進(jìn)行修改效果等等,這些操作最好先自行創(chuàng)建Word文檔進(jìn)行定義味滞,即樱蛤,通過Python腳本獲取預(yù)先定義好的Word文檔進(jìn)行插入數(shù)據(jù)的操作。
譬如剑鞍,我們有如下一張Word文檔昨凡。
我們需要填充的數(shù)據(jù)都在這個(gè)表單里,通過預(yù)先確定的唯一標(biāo)示符(即圖中的英文單詞)將Excel中的數(shù)據(jù)插入到對(duì)應(yīng)的單元格中蚁署。
//python-docx是Python較為常用的操控Word文檔的工具框架
from docx import Document
def insertCellsToWord(cells):
//獲取預(yù)先設(shè)置的模板
test = Document("XXXX模板.docx")
for t in test.tables:
for row in t.rows:
//遍歷到單元格進(jìn)行數(shù)據(jù)插入
for cell in row.cells:
try:
set_cell(cell,cells)
except Exception as e:
print(cells)
print(e)
test_number = str(cells[0].value)
//生成新模板
test.save(output_path + "普通/" + test_number + ".docx")
def set_cell(cell,cells):
//找到對(duì)應(yīng)的單元格進(jìn)行數(shù)據(jù)插入
if cell.text == "school":
cell.text = cells[13].value
需要注意的是便脊,當(dāng)你如此完成了插入數(shù)據(jù)的操作并預(yù)覽輸出的Word文檔時(shí),你會(huì)發(fā)現(xiàn)光戈,輸出的文檔并沒有按照你預(yù)先調(diào)制的展示效果展示哪痰,例如單元格中的文字居中,加粗久妆,字體修改等等晌杰,通過python-docx的操作會(huì)丟失這部分?jǐn)?shù)據(jù),cell.text = cells[13].value
的操作并不會(huì)繼承效果筷弦,因此我們需要通過python-docx再次對(duì)單元格效果進(jìn)行設(shè)置肋演。
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Pt
if cell.text == "school":
cell.text = cells[13].value
p = cell.paragraphs[0]
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.runs[0]
r.bold = True
f = r.font
f.size = Pt(12)
通過單元格的paragraphs屬性及runs屬性進(jìn)行樣式設(shè)置,在此烂琴,我設(shè)置了單元格居中爹殊,文字加粗以及字體大小設(shè)置為12,你可以在python-docx的官方文檔找到更多可參考的樣式屬性奸绷。
接下來開始進(jìn)行Word文檔合并边灭。
在合并之前需要說明的是,我們常常會(huì)遇到表單中需要插入圖片的需求健盒,比如每張表格都有一個(gè)一寸照之類的绒瘦,但在我構(gòu)寫這個(gè)腳本的踩坑中發(fā)現(xiàn)称簿,圖片并不能在合并文檔之前插入(也可能是我沒找到正確的方式),當(dāng)先插入圖片再進(jìn)行合并時(shí)惰帽,導(dǎo)出的合并文件必然是損壞的憨降,而導(dǎo)出沒有圖片的純文本合并文件卻不會(huì)損壞,因此该酗,我通過在第一次對(duì)單元格的文本設(shè)置中對(duì)標(biāo)注插入圖片的單元格替換唯一標(biāo)示符(例如上圖中的Icon變?yōu)镮con+“對(duì)應(yīng)的人員編號(hào)”)授药,然后在合并文件之后再次遍歷單元格插入圖片,理所應(yīng)當(dāng)?shù)奈仄牵@樣會(huì)導(dǎo)致腳本的效率大大降低悔叽,如果有更好的方式希望不吝賜教。
normal_path = output_path + "普通/"
//獲取本地所有待合并文檔
files = os.listdir(normal_path)
files.sort()
normal_merge_docx = ""
for index, filename in enumerate(files):
if index == 0:
normal_merge_docx = Document(normal_path + filename)
continue
sub_doc = Document(normal_path + filename)
for el in sub_doc.element.body:
normal_merge_docx.element.body.append(el)
normal_merge_docx.save("normal_merge_docx.docx")
可能會(huì)有人問爵嗅,為什么要獲取第一個(gè)待合并文件對(duì)接下來的文檔進(jìn)行合并而不是新建一個(gè)Word文檔娇澎,對(duì)所有的待合并文件進(jìn)行合并呢?
原因在于睹晒,新建的Word文檔并不包含一些高于表格的樣式數(shù)據(jù)趟庄,如果使用新建文檔對(duì)所有待合并文檔合并,則預(yù)覽輸出文件會(huì)發(fā)現(xiàn)合并Word文檔的樣式錯(cuò)誤伪很,所以必須取一個(gè)待合并文檔作為基類來合并其他文檔戚啥,這樣才保證不會(huì)樣式丟失。
接著進(jìn)行圖片插入:
from docx.shared import Inches
//因?yàn)槲倚枰迦氲膱D片位置是確定的锉试,所以我直接設(shè)置入?yún)⑷ト?duì)應(yīng)的單元格插入圖片猫十,增加效率。
def insertImage(filename,row,col):
output = Document(filename)
for t in output.tables:
cell = t.cell(row,col)
if "-" in cell.text:
print(cell.text)
arr = cell.text.split("-")
cell.text = ""
p = cell.paragraphs[0]
r = p.runs[0]
pathstr = ""
//getImage通過具體的標(biāo)識(shí)符去取本地圖片呆盖,若本地圖片有問題則通過dealImg對(duì)圖片處理再行插入
try:
pathstr = getImg(arr[0], arr[1])
except Exception as e:
print(e)
pathstr = dealImg(getImg(arr[0], arr[1]), arr[1])
r.add_picture(pathstr, width=Inches(0.84), height=Inches(1.2))
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
需要注意的是拖云,python-docx的文檔告訴我們,插入圖片的方法在單元格的paragraphs
數(shù)組屬性的runs
數(shù)組屬性中絮短,只有找到具體的run屬性才能add_picture
。
這里有一個(gè)問題昨忆,雖然在上述代碼中我們將對(duì)應(yīng)單元格的標(biāo)記文本(Icon-XXXXXXX)置為空丁频,但對(duì)應(yīng)的樣式屬性依然存在,如果通過cell
的add_paragraph
增加paragraph
再通過add_run
增加run
的樣式屬性邑贴,則會(huì)發(fā)現(xiàn)輸出文檔的圖像上多出一行席里,這是因?yàn)檫@個(gè)單元格的paragraphs數(shù)組變大了,即有兩組樣式屬性對(duì)應(yīng)不同文本拢驾,而在python-docx中又無法在不銷毀單元格的情況下對(duì)單元格內(nèi)的文本及樣式屬性進(jìn)行快速清空奖磁,因此,就需要在原來的標(biāo)記文本置為空的情況下獲取對(duì)應(yīng)的樣式屬性進(jìn)行插入圖片繁疤。
最后輸出文件:
output.save(filename)
整個(gè)流程便結(jié)束了咖为。