Word表格信息提取汇总至Excel

因为这个人办公过程中,接到了收集几十个Word申请表,还需要我自己把里面的信息一条条复制粘贴到Excel表格里面的离谱工作,所以有了本篇代码。

代码注释非常详细,而且安装好库就能用,所以就少写点啦

需要的库不多:pandas,python-docx,pywin32。使用pip或者conda可以直接安装

当然,再好用的东西也得你自己告诉他你要什么是不是?

所以: 你的word在哪儿:files_path

给一张空表参考一下:Ffile

你想让它汇总什么:need_keys

你的逆天命名方式总有一些相似的地方吧:name_keys

它应该会持续更新,我也在代码里面留下了一些我忘记了是做什么用的变量,它们不会影响使用,但最好不要动它们。

# -*-coding:utf-8-*-
import os
import pandas as pd
from docx import Document
from win32com import client as wc


files_path  = r'C:\Users\Desktop\code\work'
Ffile = r'C:\Users\Desktop\code\表.docx'
need_keys = '姓 名,性别,手 机,备注'
name_keys = '申请表'

""" 本代码建立在python-docx包的合并单元格
    实际储存格式为子单元格存有相同数值的基础上
    当这一数据结构改变时,本代码需要更新,
    否则将不再具有使用价值
    暂时只能读一个word中只有一个表格的情况
    python-docx基于储存格式和dict不允许相同key的基础上,
    所需要素:
            1.未填信息的word空表(格式文件)
            2.需要汇总的信息,以中文逗号分隔
            如'姓名,性别,联系方式'
            3.文件夹所在路径
            4.文件名关键字
    代码原理如下:
    将原本的word空表(即格式文件)读入字典,在字典中建立列表
    遍历列表,填充字典
    删除不需要的信息
    2023.02.22更新,用列表替换原本的字符串,
    解决了如:属性(是否大帅比)值(是)引起的读取错误
"""
class read_table:
    def __init__(self, file_path, keys, format_file, name_keys):
        self.file_path = file_path  # 文件最大父目录
        self.keys = keys  # 所需信息
        self.format_file = format_file  # 格式文件
        self.name_keys = name_keys  # 文件名关键字

    def build_list(self, name_keys):  # 文件列表建立函数
        file_list = []  # 文件列表
        word = wc.Dispatch("Word.Application")  # 调用Word程序,用以完成doc转docx功能
        for root, dirs, files in os.walk(self):  # 遍历所有文件名
            for name in files:
                if name.startswith('~$'):  # 跳过临时文件
                    continue
                elif name.split('.')[-1] == 'doc':  # 若为doc 转换文件格式为docx
                    name = os.path.join(root, name)  # 获取文件路径
                    docx = word.Documents.Open(name)  # 打开word文件
                    docx.SaveAs("{}x".format(name), 12)  # 另存为后缀为".docx"的文件,其中参数12指docx文件
                    docx.Close()  # 关闭原来word文件
                    os.remove(name)  # 移除原有doc文件
                    if name_keys =='':   # 2023.02.22更新,可以不指定文件名中的关键字
                        file_list.append("{}x".format(name))
                    else:
                        for key in name_keys.split(','):  # 判断该word文件是否为需要的文件
                            if name.find(key) != -1:
                                file_list.append("{}x".format(name))  # 记录文件名
                                break
                elif name.split('.')[-1] == 'docx':  # docx直接进入判断
                    if name_keys == '':    #同上
                        file_list.append(os.path.join(root, name))
                    else:
                        for key in name_keys.split(','):
                            if name.find(key) != -1:
                                file_list.append(os.path.join(root, name))
                                break
        word.Quit()  # 遍历结束,退出word程序
        print('*' * 20)
        print('程序初始化完成!')
        print('*' * 20)
        return file_list  # 返回文件列表

    def read(self):  # 主函数
        file_list = read_table.build_list(self.file_path, self.name_keys)  # 建立文件列表
        main_dict = {}  # 建立数据字典
        rows = Document(self.format_file).tables[0].rows  # 读取格式文件中的表格
        for row in rows:  # 读取属性,以字典键的形式储存
            for cell in row.cells:
                if cell.text != '':
                    main_dict[cell.text] = []
        check_str = []  # 字典属性记录字符串
        dim_str = ''  # 多余属性记录字符串
        for key in main_dict.keys():
            check_str.append(key)
        for each_file in file_list:  # 对于每个文件
            print('开始读取文件:')
            print(each_file)
            rows = Document(each_file).tables[0].rows  # 获取表格
            i = 0  # 行号
            j = 0  # 列号
            d = 0  # 值,竖向搜索控制
            k = 0  # 值,横向搜索控制
            key = ''  # 当前属性
            while i < len(rows):
                # 开始遍历表格,循环判断条件并没有什么作用,只是还没有进行代码重构
                # 循环的正常进行由代码中的continue和break来控制
                if d == 0:  # 好吧,我也不知道它是干啥的1111111111111111111111111111111111111111111111111111111111111
                    j = 0
                while j < len(rows[i].cells):  # 循环的作用是保证实现任务目标前代码不会终止
                    if d == 0 and k != 1:  # 正常遍历
                        if not rows[i].cells[j].text in check_str or rows[i].cells[j].text == key:
                            j += 1  # 上述的判断保证了值和重复关键字不会被作为中心单元格
                            continue
                        else:  # 以当前格子的文本为属性
                            key = rows[i].cells[j].text
                        if j == len(rows[i].cells) - 1:  # 当这个格子是改行的最后一个格子时,需要特殊对待
                            if rows[i].cells[j - 1] == rows[i].cells[j]:
                                break  # 该格子的值和前一个格子相等,下一行
                            elif not rows[i].cells[j].text in check_str:
                                main_dict[key].append(rows[i].cells[j].text)
                                i += 1  # 当前格子是个值,记录一下,下一行
                                break
                            else:
                                d = 1  # 当前格子是个新属性,向下搜索
                                continue
                        if rows[i + 1].cells[j].text in check_str:  # 当属性下方还是属性,
                            if key == rows[i + 1].cells[j].text:  # 它们还相等
                                if rows[i].cells[j + 1].text in check_str:
                                    j += 1  # 右侧也是个属性,下一格
                                    continue
                                elif rows[i].cells[j + 1].text == rows[i + 1].cells[j + 1].text:
                                    main_dict[key].append(rows[i].cells[j + 1].text)
                                    j += 1  # 右边和右下角是两个相等的值,记录,下一格,下一个属性
                                    key = ''
                                    continue
                            else:  # 它们不相等
                                if rows[i].cells[j + 1].text in check_str:
                                    key = ''  # 右侧是个属性,下一格,下一个属性
                                    j += 1
                                    continue
                                else:
                                    main_dict[key].append(rows[i].cells[j + 1].text)
                                    j += 1  # 右侧是个值,记录,开始横向搜索
                                    k = 1
                                    continue
                        else:  # 属性下方是个值,记录,竖向搜索
                            main_dict[key].append(rows[i + 1].cells[j].text)
                            i += 1
                            d += 1  # 向下搜索
                            continue
                    elif k == 1:  # 横向搜索中,
                        if rows[i].cells[j].text == rows[i].cells[j + 1].text or (
                                rows[i].cells[j + 1].text in check_str):
                            key = ''  # 右侧的值和当前值相等或者右侧是个属性,
                            k = 0  # 下一格,下一个属性,终止横向搜索
                            j += 1
                            continue
                        else:  # 记录该值,继续横向搜索
                            main_dict[key].append(rows[i].cells[j + 1].text)
                            j += 1
                            continue
                    else:  # 竖向搜索中
                        if not rows[i + 1].cells[j].text in check_str:  # 值下方是个值
                            if j != len(rows[i + 1].cells) - 1:  # 不是最后一格
                                if rows[i + 1].cells[j].text != rows[i + 1].cells[j + 1].text:
                                    main_dict[key].append(rows[i + 1].cells[j].text)
                                    i += 1  # 下方的值与右下角的值不等,记录值,继续向下
                                    d += 1  # 向下搜索
                                    continue
                                else:  # 终止向下搜索,返回开始位置,下一格
                                    i = i - d
                                    j += 1
                                    d = 0
                                    continue
                            else:  # 最后一格时,因为右侧无法进行判断,与左侧格子进行对比
                                if rows[i + 1].cells[j - 1].text != rows[i + 1].cells[j].text:
                                    main_dict[key].append(rows[i + 1].cells[j].text)  # 记录值
                                    i += 1
                                    d += 1  # 向下搜索
                                    continue
                                else:
                                    i = i - d + 1
                                    d = 0
                                    j += 1
                        else:  # 值下方是个属性,终止向下搜索
                            if j == len(rows[i].cells) - 1:
                                i = i - d + 1
                            else:
                                i = i - d
                            j += 1
                            d = 0
                            continue
                i += 1  # 一行走完了就要下一行啊
                if i == len(rows) - 1:  # 最后一行不能和下方单元格进行判断
                    j = 0
                    if not rows[i].cells[0].text in check_str or \
                            rows[i].cells[0].text == rows[i - 1].cells[0].text:
                        # 开头是个值,或者当前格子的内容与上方格子内容一样,终止搜索
                        break
                    else:
                        while j < len(rows[i].cells) - 1:
                            if rows[i].cells[j].text in check_str:  # 当前格子是个属性
                                if not rows[i].cells[j + 1].text in check_str:  # 右侧和它不相等,记录,下一格
                                    main_dict[rows[i].cells[j].text].append(rows[i].cells[j + 1].text)
                                j += 1
                            else:
                                j += 1
                        break
            check_keys = []  # 记录字典属性
            for key in main_dict.keys():
                check_keys.append(key)
            i = 0
            j = 1
            while i < len(check_keys) - 1:  # 对比字典属性
                if check_keys[i].find(check_keys[j]) != -1:  # 若某属性被其他属性所包含
                    if dim_str.find(check_keys[j]) == -1:  # 记录该属性
                        dim_str += check_keys[j] + ','
                    for value in main_dict[check_keys[j]]:  # 将子属性值追加至父属性值列表,清空子属性
                        if value != '':
                            main_dict[check_keys[i]].append(value)
                            main_dict[check_keys[j]].remove(value)
                    j += 1
                elif check_keys[j].find(check_keys[i]) != -1:
                    if dim_str.find(check_keys[i]) == -1:
                        dim_str += check_keys[i] + ','
                    for value in main_dict[check_keys[i]]:
                        if value != '':
                            main_dict[check_keys[j]].append(value)
                            main_dict[check_keys[i]].remove(value)
                    j += 1
                else:
                    j += 1
                if j >= len(check_keys) - 1:
                    i += 1
                    j = i + 1
            list_length = 0  # 属性值列表长度最大值
            for key in main_dict.keys():
                if list_length < len(main_dict[key]):
                    list_length = len(main_dict[key])
            for key in main_dict.keys():  # 用空值填补较短列表
                while len(main_dict[key]) < list_length:
                    main_dict[key].append('')
        print('*' * 10)
        print('文件读取完毕,开始写入结果!')
        print('*' * 10)
        final_check = []  # 记录列表属性
        for key in main_dict.keys():
            final_check.append(key)
        for table_key in final_check:  # 对每个列表属性,若不被需要,删除
            n = 0
            if self.keys.find(table_key) == -1:
                for key in dim_str.split(','):
                    if table_key.find(key) != -1 and key != '':
                        del main_dict[key]
                        n = 1
                        break
                if n == 0:
                    del main_dict[table_key]
        df = pd.DataFrame(main_dict)  # 将字典写入DataFrame,保存为excel文件
        df.to_excel(os.path.join(self.file_path, 'result.xlsx'))
        print('结果文件已保存至:' + os.path.join(self.file_path, 'result.xlsx'))


if __name__ == "__main__":
    T = read_table(files_path,need_keys ,
                   Ffile, '')
    T.read()

既然你看到这儿了,说明你可能要参考或者要用这个东西,有什么问题或者建议记得告诉我哈,

邮箱在这儿:mrlonely0@163.com

高情商:我的python还有非常大的进步空间

低情商:我啥都不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值