Python-Excel转word表格并合并流程


工作中正好接到个小项目需要将Excel数据导出组成word表格并合并所有表格成一个word文档,这个流程涉及到Excel的解析、Word文档的数据插入,多个Word文档的合并,对Word文档插入图片等,在尝试构写脚本时发现百度谷歌的资料多有缺失或者干脆就是过时的方法,于是写下这篇文章将自己在构写这个脚本时遇到的坑写一下。

首先是解析Excel数据,Python的xlrd框架可以快速地帮助我们完成这一部分操作:

import xlrd

//获取Excel文档
workbook = xlrd.open_workbook(u'2019名单.xls')

//我们可以通过两种不同的方式获取Excel文档中对应的表单
sheet = workbook.sheets()[0]
sheet = getSheetByName("表单名字",workbook)

//通过xlrd的接口获得表单的行数及列数
nrow = sheet.nrows
ncol = sheet.ncols

for i in range(1,nrow):
//cells代表的是Excel表单的每一行,即一个单元格组
    cells = sheet.row(i)
    //得到单元格组我们便可执行下一步的插入Word文档操作了
    insertCellsToWord(cells)

需要明白的是,最好不要在Python脚本中对Word文档的效果进行再定义,原因之一是因为脚本并不全能,如果在脚本中对文档效果进行再定义,代码会变得臃肿且难以维护,例如先通过脚本生成表单效果再对单元格进行修改效果等等,这些操作最好先自行创建Word文档进行定义,即,通过Python脚本获取预先定义好的Word文档进行插入数据的操作。
譬如,我们有如下一张Word文档。


我们需要填充的数据都在这个表单里,通过预先确定的唯一标示符(即图中的英文单词)将Excel中的数据插入到对应的单元格中。

//python-docx是Python较为常用的操控Word文档的工具框架
from docx import Document

def insertCellsToWord(cells):
    //获取预先设置的模板
    test = Document("XXXX模板.docx")
    for t in test.tables:
        for row in t.rows:
            //遍历到单元格进行数据插入
            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):
    //找到对应的单元格进行数据插入
    if cell.text == "school":
        cell.text = cells[13].value

需要注意的是,当你如此完成了插入数据的操作并预览输出的Word文档时,你会发现,输出的文档并没有按照你预先调制的展示效果展示,例如单元格中的文字居中,加粗,字体修改等等,通过python-docx的操作会丢失这部分数据,cell.text = cells[13].value的操作并不会继承效果,因此我们需要通过python-docx再次对单元格效果进行设置。

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属性进行样式设置,在此,我设置了单元格居中,文字加粗以及字体大小设置为12,你可以在python-docx的官方文档找到更多可参考的样式属性。

接下来开始进行Word文档合并。
在合并之前需要说明的是,我们常常会遇到表单中需要插入图片的需求,比如每张表格都有一个一寸照之类的,但在我构写这个脚本的踩坑中发现,图片并不能在合并文档之前插入(也可能是我没找到正确的方式),当先插入图片再进行合并时,导出的合并文件必然是损坏的,而导出没有图片的纯文本合并文件却不会损坏,因此,我通过在第一次对单元格的文本设置中对标注插入图片的单元格替换唯一标示符(例如上图中的Icon变为Icon+“对应的人员编号”),然后在合并文件之后再次遍历单元格插入图片,理所应当的,这样会导致脚本的效率大大降低,如果有更好的方式希望不吝赐教。


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

可能会有人问,为什么要获取第一个待合并文件对接下来的文档进行合并而不是新建一个Word文档,对所有的待合并文件进行合并呢?

原因在于,新建的Word文档并不包含一些高于表格的样式数据,如果使用新建文档对所有待合并文档合并,则预览输出文件会发现合并Word文档的样式错误,所以必须取一个待合并文档作为基类来合并其他文档,这样才保证不会样式丢失。

接着进行图片插入:

from docx.shared import Inches

//因为我需要插入的图片位置是确定的,所以我直接设置入参去取对应的单元格插入图片,增加效率。
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通过具体的标识符去取本地图片,若本地图片有问题则通过dealImg对图片处理再行插入
            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数组属性的runs数组属性中,只有找到具体的run属性才能add_picture
这里有一个问题,虽然在上述代码中我们将对应单元格的标记文本(Icon-XXXXXXX)置为空,但对应的样式属性依然存在,如果通过celladd_paragraph增加paragraph再通过add_run增加run的样式属性,则会发现输出文档的图像上多出一行,这是因为这个单元格的paragraphs数组变大了,即有两组样式属性对应不同文本,而在python-docx中又无法在不销毁单元格的情况下对单元格内的文本及样式属性进行快速清空,因此,就需要在原来的标记文本置为空的情况下获取对应的样式属性进行插入图片。

最后输出文件:

output.save(filename)

整个流程便结束了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343