记excel批量生成word文件

前言

受到目前所在公司的影响,经常和简历打交到。虽然本职工作是 PHP 接口开发,但更多像是在打杂(手动滑稽,哈哈哈),那里需要就去那里搬砖。

不定期的,销售或者客户支持人员会线下接到客户给的离线简历,要求我们给他导入到系统,直接给 word 或者 PDF 的这种还好,给 excle 这种是真心艹蛋。

为啥?很多公司或者用人单位都会自建或购买HR系统,简历会录入到公司的系统中。自制手写的简历一般HR会自己入到系统中去,而投递的 word 文档类型的简历则会用到简历解析服务,也就是文本识别,将 word 文件类的简历解析成方便自己公司数据库存储的字段。

我是见过很多不友好的简历,比如一份 word 简历内直接贴图片的,关键信息用 文本框 做定位然后在里面写内容等等。

写简历的是爽了,布局排版看起来花里胡哨的,最后简历解析的工程师(我军)被喷成塞子。

A:你们的解析系统不好用啊,10份简历上传9份解析错误?
我方:......(内心是崩溃的,10个中五个是mht,还有5个是rtf,这能解出来一个就不错了)
B:这个html刚从5*同城保存下来的,为什么手机号解析不出来啊?
我方:......(人家加了防爬虫,手机号都被JS转了,免费版还指望给你维护这个?)
C:姓名解析错了呀,他不叫XXX应该叫XX才对呀
我方:......(一个文件里出现四五个姓名的,要拿那个?啊拿那个)

而这种 excle 是真的艹, 里面塞万把个简历也就算了,关键的教育经历和工作经历的字段也不分列存的,多段数据装在一个单元格里,当字符串存储。内容都不能保证规范还指望入库能百分百将它分开识别到对应字段啊?

所以呢,每次都要将 excle 中的内容取出来,生成对应的 docx 文档,然后在走解析服务,拿到解析后的详情字段在录入到系统中去。

这里记录下,我是怎么处理这些简历的。

数据量少用word邮箱合并

新建Word文档充当模版

这样做可以将 excle 中的多行数据,批量导出成有固定排版的 docx 文档,每一行数据就是一个 word 文件。

首先需要一个主标题,用于后面合并内容再拆成单个 word 文件。

完整的模版大致如下所示:

第一步需要导入 excle 数据,选择 邮箱->使用现有列表

导入成功后 插入合并域 才会亮起。

将光标停在想要插入位置,选择对应的 excel 的标题列即可。

插入完之后,我么可以点击 预览结果 查看最终导出是的效果。

合并后导出多个文件

模版调好后,需要操作 完成并合并 -> 编辑单个文件

此时会将 excle 中所有列中的数据合并成一个文档,我们通过查看 文档结构图 确认下。

接下来就是最后一步导出了,先在进入 视图->大纲 模式。

然后在文档内 Ctrl+A 全选所有内容,选择保存。

比如我保存为 总表.docx,随后在保存的文件目录会出现一个同名的文件夹,里面就是我们最终批量导出 docx 文件。

是不是没有后缀名?不用担心,一条指令搞定

$ for i in *;do mv "$i" "${i}.docx" ;done

数据太多直接写python并行脚本

上面的那种只适用于数据量小的场景,要是有几万的 excle 数据就会变得不适用了。

Mac上应该是有office应用的内存使用上限设置的,超过这个数就会报错说资源不够(我测的数据为3万列)。

然后我又在家里拿出的闲置的雷神911,Windows下虽然不会报错,但处理的时间非常长,仅在 使用现有列表 就耗时了1个小时还看不到头,果断放弃了。

所以想了想,程序员就得用程序员的方法解决。

写-脚-本

# -*- coding: utf-8 -*-
__author__ = 'xjiek2010<at>icloud.com'

import xlrd, sys, os, docx, shutil, threading, time

reload(sys)
sys.setdefaultencoding('utf-8')


# 遍历所有文件夹内文件
def getAllFilePaths(path):
    files = os.listdir(path)
    for name in files:
        tmp_path = os.path.join(path, name)
        if not os.path.isdir(tmp_path):
            # print '文件: %s' % tmp_path
            allDirs.append(tmp_path)
        else:
            # print '文件夹: %s' % tmp_path
            getAllFilePaths(tmp_path)


def formatData(path_xlsx):
    # 处理指定后缀文
    if 'xlsx' == path_xlsx[-4:]:
        data = xlrd.open_workbook(path_xlsx)  # 打开xls文件
        table = data.sheets()[0]  # 打开第一张表
        nrows = table.nrows  # 获取表的行数

        for i in range(nrows):  # 循环逐行打印
            document = docx.Document()
            if i == 0:  # 跳过第一行
                continue
            row = table.row_values(i)  # 取所有列数据
            document.add_heading(ur'(' + str(row[11]) + ur')个人简历', 0)  # 标题

            document.add_paragraph(ur'姓名:' + row[11])
            document.add_paragraph(ur'邮箱:' + row[22])
            document.add_paragraph(ur'手机号:' + row[24])
            document.add_paragraph(ur'出生年月:' + row[13])
            document.add_paragraph(ur'性别:' + row[12])
            document.add_paragraph(ur'目前居住地:' + row[15])
            document.add_paragraph(ur'户口所在地:' + row[17])
            document.add_paragraph(ur'期望行业:' + row[29])
            document.add_paragraph(ur'期望工作地:' + row[30])
            document.add_paragraph(ur'期望薪资:' + row[32])
            document.add_paragraph(ur'工作经历:' + row[33])
            document.add_paragraph(ur'项目经历:' + row[34])
            document.add_paragraph(ur'教育经历:' + row[35])
            document.add_paragraph(ur'获奖经历:' + row[36])
            document.add_paragraph(ur'培训经历:' + row[38])
            document.add_paragraph(ur'IT技能:' + row[39])
            document.add_paragraph(ur'获取证书:' + row[40])
            document.add_paragraph(ur'附加信息:' + row[41])
            document.add_paragraph(ur'自我评价:' + row[27])
            if not os.path.exists(save_path):
                os.mkdir(save_path)
            document.save(save_path + '/' + row[9] + ur'.docx')  # 生成 简历号.docx

        # 完成后移动原数据文件
        if not os.path.exists(ur'ok/投递/' + path_xlsx.split('/')[2] + '/'):
            os.mkdir(ur'ok/投递/' + path_xlsx.split('/')[2] + '/')
        shutil.move(path_xlsx, ur'ok/投递/' + path_xlsx.split('/')[2] + '/')
        print '[+] 完成移动文件:' + path_xlsx
    else:
        pass


# 创建一个类,必须要继承Thread
class MyThread(threading.Thread):
    def __init__(self, threadID, name, s):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.s = s  # 列表索引参数

    def run(self):
        # threadLock.acquire() # 获得锁
        for arrKey in range(self.s, self.s + gap):
            formatData(allDirs[arrKey])
            flog.write('[+] ' + allDirs[arrKey] + '\r\n')
            if stop:
                print '[+] %s收到结束信号正在处理' % allDirs[arrKey]
                break
        print '%s已结束' % self.name
        # threadLock.release() # 释放


if __name__ == "__main__":
    stop = False
    start_time = time.time()

    allDirs = []  # 所有文件的字典
    flog = open('log.txt', 'w+', buffering=0)

    path = 'insetFiles/xxx投递拆分'
    save_path = 'xxx投递'
    getAllFilePaths(path)

    totalThread = 3  # 需要创建的线程数,可以控制线程的数量
    lenList = allDirs.__len__()  # 总文件计数
    gap = lenList / totalThread  # 列表分配到每个线程的执行数
    # threadLock = threading.Lock()  # 锁

    # 多线程开始
    thread_list = []

    for i in range(totalThread):
        thread = 'thread%s' % i
        if i == 0:
            # thread = MyThread(0, "Thread-%s" % i, 0, gap)
            thread = MyThread(0, 'Thread-%s' % i, 0)
        elif totalThread == i + 1:
            # thread = MyThread(i, "Thread-%s" % i, i * gap, lenList)
            thread = MyThread(i, 'Thread-%s' % i, i * gap)
        else:
            # thread = MyThread(i, "Thread-%s" % i, i * gap, (i + 1) * gap)
            thread = MyThread(i, 'Thread-%s' % i, i * gap)
        thread_list.append(thread)  # 添加线程到列表

    # 循环开启线程
    for i in range(totalThread):
        thread_list[i].start()
    try:
        while True:
            pass
    except KeyboardInterrupt as e:
        print '收到结束信号,正在处理'
        stop = True

    # 等待所有线程完成
    for t in thread_list:
        t.join()
    print('[+] 所有子线程已结束')
    print '[+] 共计用时%f秒' % (time.time() - start_time)

上面是参考网上查到的资料写的代码,主要是用 threading 多线程处理 excle 内的数据,将其提取出来生成 .docx 文件。

之前都没有玩过 threading,在 Control + C 无法退出脚本这里坑了一下。不过还好,最终找到解决的方法,克服了它。

之前也写过 PHP 的并行,用的是 pclosepopen 两个函数结合SHELL传参,实现了一秒下载300章节的小说至本地,哈哈哈

参考

https://www.grassfish.net/2017/03/27/python-thread-handle-sigint/

https://zhuanlan.zhihu.com/p/34004447

http://www.laruence.com/2008/04/14/318.html


版权声明

除非另有说明,本网站上的内容均根据 Creative Commons Attribution-ShareAlike License 4.0 International (CC BY-SA 4.0) 获得许可。