python自动化处理word文档,生成表格到Excel

本文的目的仅用于记录在用python自动化处理word文档,将文档中文本以及表格提取到excel中的相关操作以及问题
问题描述:由于word文档篇幅较多,格式复杂,后续查看与修改工作不方便,因此考虑将其中的表格提取出来,根据表格的名称设置sheet提取到excel中。由于格式不同,且数据量很大,故采用自动化的方式进行处理
问题解析:原word文档包含大量的软件测试test case,首先提取word中该test case的ID或名称作为excel工作表的sheetname,其次将对应的表格放入sheetname中。考虑到sheet过多,因此建立目录sheet,设置相关超链接便于索引,在相应的页面建立返回目录的超链接。

0. 解除word编辑限制

将文档.docx文件用word打开,另存为.xml文件
用记事本打开.xml文件修改内容。
找到documentProtection字段,将"DocumentProtection"改为"unDocumentProtection",保存即可。

如这个例子 <w:documentProtection w:edit=“forms” w:formatting=“1” w:enforcement=“0”/>

或者另外不同xml版本显示有可能是这样的:<w:DocumentProtection>forms</w:DocumentProtection>

word打开xml文件,另存为.docx文件。

1. 解析word文档

首先需要采用python读取word文档,需要安装python相关库

pip install python-docx

通过如下命令对word文档中的段落以及表格进行读取

import docx
# 建立word对象,其成员变量和成员函数参考如下官方链接
# https://python-docx.readthedocs.io/en/latest/api/document.html#document-constructor
doc = docx.Document('your word file.docx')

# 读取段落并打印段落文本
paragraphs = doc.paragraphs
for paragraph in paragraphs:
    print(paragraph.text)        # 打印段落文本
    print(paragraph.style.name)  # 打印段落的类型,是属于几级标题(Heading x)还是正文(Normal)

# 读取所有表格并依次打印
tables = doc.tables
for table in tables:
    print(f"Table: {table}")
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)
        print("")
    print("=" * 20)

问题:
word文件是由其它软件导出,设置有内容控件,python无法读取这些有内容控件段落和文本(暂时没有找到直接通过python解决的方案)
只能一次性提取表格或者段落,无法按照word文件的原本顺序对段落和表格进行依次提取。
解决方案:

问题1:删除内容控件

内容控件理解为一个个的模块,将word中的相关信息封装成一个模块。
如下图所示,当你选中’work_item’,这个模块里面的段落和表格都会被选中,却可以整体移动。
内容控件可以进行嵌套,就像函数一样。
word-内容控件示例

  1. 打开word的开发者模式
    点击word左上角’File’–>左下角’Options’,进入如下页面:
    勾选Developer,返回主页面即可看到Developer菜单栏。
    打开Developer选项
    Developer菜单栏

选择一个content control,点击Developer–>Properties
查看’Locking’是否都被勾选,
第一个被勾选:内容控件无法被删除
第二个被勾选:内容控件无法被编辑
此时就算取消勾选,也只能删除一个内容控件,无法全部删除

内容控件属性

若要删除文本所有的内容控件,先选中你要处理的word文档内容。
点击Developer–>Design Mode–>Properties
点击Design Mode后出现了所有内容控件,此时再将Properties的Locking全部取消勾选
然后选中要删除内容控件,右击鼠标,点击’Remove Content Control’. 即可删除所有内容控件,包括嵌套的。
在这里插入图片描述
移除内容控件

问题2:顺序提取word文档中段落和表格

要将段落与表格顺序提取,参考
顺序提取word
原理大概就是提取paragraphs和tables的父类然后进行一堆什么判断,不管这么多,能用就行。

import docx
from docx.document import Document
from docx.text.paragraph import Paragraph
from docx.table import _Cell, Table
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P

def iter_block_items(parent):
    """
    Generate a reference to each paragraph and table child within *parent*,
    in document order. Each returned value is an instance of either Table or
    Paragraph. *parent* would most commonly be a reference to a main
    Document object, but also works for a _Cell object, which itself can
    contain paragraphs and tables.
    """
    if isinstance(parent, _Document):
        parent_elm = parent.element.body
        # print(parent_elm.xml)
    elif isinstance(parent, _Cell):
        parent_elm = parent._tc
    else:
        raise ValueError("something's not right")

    for child in parent_elm.iterchildren():
        if isinstance(child, CT_P):
            yield Paragraph(child, parent)
        elif isinstance(child, CT_Tbl):
            yield Table(child, parent)

使用案例参考:

doc = docx.Document("/path/to/your/word.docx")

for block in iter_block_items(doc):
    if isinstance(block,Table):
        #this is a table 
        #do something here
    else: 
        #this is a paragraph
        #do something else here

如果增加print可以看到,段落和表格会按照word原文顺序进行输出

2. 根据章节拆分word

为了减少操作出错,且便于文件查看,根据word的章节标题将word拆分为多个子文件,子文件名为对应的章节名称,文档内其余保持不变。

word文档拆为子文件

import os
import docx
import pandas as pd

from docx.document import Document
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.table import _Cell, Table
from docx.text.paragraph import Paragraph
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT

from cell_board_set import *

def iter_block_items(parent):
    if isinstance(parent, Document):
        parent_elm = parent.element.body
    elif isinstance(parent, _Cell):
        parent_elm = parent._tc
    else:
        raise ValueError("something's not right")

    for child in parent_elm.iterchildren():
        if isinstance(child, CT_P):
            yield Paragraph(child, parent)
        elif isinstance(child, CT_Tbl):
            yield Table(child, parent)

def read_table(table):
    table_data = []
    for row in table.rows:
        row_data = [cell.text for cell in row.cells]
        table_data.append(row_data)
        # table_df = pd.DataFrame(table_data)
        # print(table_df)
    return table_data


def create_new_document(title, content):
    new_doc = docx.Document()
    new_doc.add_heading(title, level=1)

    for para in content:
        if isinstance(para, Paragraph):
            if 'Heading' in para.style.name:
                level = int(para.style.name.split()[-1])
                new_doc.add_heading(para.text, level=level)
            else:
                new_doc.add_paragraph(para.text)

        elif isinstance(para, list):
            table_df = pd.DataFrame(para)
            table = new_doc.add_table(rows=len(para), cols=len(para[0]))

            # 设置表头样式
            header_row = para[0]

            # 设置表头样式
            for j, cell_value in enumerate(header_row):
                
                cell = table.cell(0, j)
                cell.text = str(cell_value)
                cell.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER  # 设置居中对齐
                cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER  # 设置垂直居中
                cell.paragraphs[0].runs[0].bold = True  # 设置粗体

                set_cell_border(cell,
                    top={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                    bottom={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                    start={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                    end={"sz": 12, "val": "single", "color": "#000000", "space": "0"},)

            # 设置表格内容样式
            for i, row in enumerate(para[1:], 1):
                for j, cell_value in enumerate(row):
                    cell = table.cell(i, j)
                    cell.text = str(cell_value)
                    set_cell_border(cell,
                        top={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                        bottom={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                        start={"sz": 12, "val": "single", "color": "#000000", "space": "0"},
                        end={"sz": 12, "val": "single", "color": "#000000", "space": "0"},)
    return new_doc

def read_word(word_path):
    doc = docx.Document(word_path)
    current_title = None
    current_content = []

    for block in iter_block_items(doc):
        if isinstance(block, Paragraph):
            if block.style.name == "Heading 1":
                print('Current Chapter: ', block.text)

                # 新一级标题出现,保存上一个标题的内容为新的 docx 文件
                if current_title:
                    # print('current_content: \n', current_content)
                    new_doc = create_new_document(current_title, current_content)
                    new_file_name = f"{current_title}.docx"
                    new_doc.save(os.path.join(output_folder, new_file_name))

                # 更新当前标题和内容列表
                # print('Update title: ', block.text)
                current_title = block.text
                current_content = []
            else:
                # 如果这一段不是空的,就加入content中
                if len(block.text) != 0:
                    current_content.append(block)
        elif isinstance(block, Table):
            table = read_table(block)
            # print('This is table:\n', table)
            current_content.append(table)

    # 保存最后一个标题的内容为新的 docx 文件
    if current_title:
        new_doc = create_new_document(current_title, current_content)
        new_file_name = f"{current_title}.docx"
        new_doc.save(os.path.join(output_folder, new_file_name))
        print('转换完毕')

if __name__ == '__main__':
    input_folder = r'path\your_document.docx'
    output_folder = r'path_to_save\folder'

    # 创建保存子文档的文件夹
    os.makedirs(output_folder, exist_ok=True)
    read_word(input_folder)

设置表格边界框

直接分割后的表格没有边界线,很不美观,参考其他博主给出word边界框的设置
对应上面的set_cell_border函数。

from docx.table import _Cell
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
 
def set_cell_border(cell: _Cell, **kwargs):
    """
    Set cell`s border
    Usage:
    set_cell_border(
        cell,
        top={"sz": 12, "val": "single", "color": "#FF0000", "space": "0"},
        bottom={"sz": 12, "color": "#00FF00", "val": "single"},
        start={"sz": 24, "val": "dashed", "shadow": "true"},
        end={"sz": 12, "val": "dashed"},
    )
    """
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
 
    # check for tag existnace, if none found, then create one
    tcBorders = tcPr.first_child_found_in("w:tcBorders")
    if tcBorders is None:
        tcBorders = OxmlElement('w:tcBorders')
        tcPr.append(tcBorders)
 
    # list over all available tags
    for edge in ('start', 'top', 'end', 'bottom', 'insideH', 'insideV'):
        edge_data = kwargs.get(edge)
        if edge_data:
            tag = 'w:{}'.format(edge)
 
            # check for tag existnace, if none found, then create one
            element = tcBorders.find(qn(tag))
            if element is None:
                element = OxmlElement(tag)
                tcBorders.append(element)
 
            # looks like order of attributes is important
            for key in ["sz", "val", "color", "space", "shadow"]:
                if key in edge_data:
                    element.set(qn('w:{}'.format(key)), str(edge_data[key]))

通过修改要分割的word文件路径以及分割后文件的保存路径,即可将文件分隔输出,且表格有边界框较为美观。

3. 提取word文本与表格信息到excel并调整

目前分割后的子文件docx已经保存到目标文件夹中,此时需对这些文本进行批量转化。

1)读取当前路径下所有的docx文件

folder_path = r'docx_folder'
output_folder = r'excel_folder'

docx_files = glob(os.path.join(folder_path, "*.docx"))
file_lists = []
for file in docx_files:
    file = file.split('\\')[-1]
    sheet_names = list()
    print(file)
    doc = docx.Document(file)
    excel_name = os.path.splitext(os.path.basename(file))[0]
    write_word2excel(doc, excel_name, output_folder)

2)转化word到excel

创建Excel工作簿
需要用到处理Excel的xlwings库

pip install xlwings

创建一个Excel文件

excel_file = excel_name + '.xlsx'
excel_path = os.path.join(output_folder, excel_file)
app = xw.App(visible=True, add_book=False)
workbook = app.books.add()
worksheet = None  # 初始化为 None

创建Excel的WorkSheet
sheet_name是自己设置的,不能超过31个字符

worksheet = workbook.sheets.add(sheet_name)

如果sheet_name超过长度限制,可以通过下面的函数对sheet_name进行处理

import re
def truncate_sheet_name(sheet_name):
    max_length = 31  # 最大长度为31个字符
    # 删除非法字符
    sheet_name = re.sub(r'[\\/*?:[\]]()', '', sheet_name)
    if len(sheet_name) > max_length:
        # 缩短名称以适应最大长度
        sheet_name = sheet_name[:max_length]
    # 将单边的括号用 "-" 代替
    sheet_name = re.sub(r'\(', '-', sheet_name)
    sheet_name = re.sub(r'\)', '', sheet_name)
    return sheet_name

读取文本到excel指定worksheet的单元格

worksheet = workbook.sheets[sheet_name] # 打开指定sheet_name的work sheet
worksheet.range('A1').value = 'anything your what input' # 如果是word中的文本,则为 'block.text'
读取表格到Excel
df = pd.DataFrame([[cell.text for cell in row.cells] for row in block.rows],
                columns=[cell.text for cell in block.rows[0].cells])

寻找空白单元格进行填充

row_offset = 0
while True:
    range_address = f'A{row_offset+3}'
    if worksheet.range(range_address).value is None:
        break
    row_offset += 1
if row_offset > 0:
    row_offset += 1  # 在已有表格后空一行
worksheet.range(f'A{row_offset+3}').options(index=False).value = df

调整单元格大小以适应内容长度

column_range = worksheet.range(f'A{row_offset+3}').expand('table')
column_range.columns.autofit()

设置超链接便于查看,同样可以建立超链接返回目录页,非常方便

创建目录工作表

catalog_name = 'Catalogs'
directory_ws = workbook.sheets.add(catalog_name)
offset = 0
directory_ws.range(f'A{1}').value = 'Catalog'
directory_ws.range(f'A{offset+3}').value = sheet_name

设置超链接

directory_ws.range(f'A{offset+3}').api.Hyperlinks.Add(
    Anchor=directory_ws.range(f'A{offset+3}').api,
    Address='',
    SubAddress=f"'{sheet_name}'!A1",
    TextToDisplay=sheet_name  # 这个超链接的显示的文本
)
offset += 1

3) 调整单元格以及表格

对单元格进行调整,使得Excel文件更加美观,直观

def adjust_worksheet(worksheet):
    if worksheet is not None:
        title_range = worksheet.range('A1')
        title_range.api.Font.Bold = True
        title_range.api.Font.Size = 14
        title_range.column_width = 20

        # 设置单元格垂直居中和水平居中
        worksheet.range('A1').vertical_alignment = 'center'
        worksheet.range('A1').horizontal_alignment = 'center'
# 表格的调节参考
# 调整单元格大小以适应内容长度
column_range = worksheet.range(f'A{row_offset+3}').expand('table')
column_range.columns.autofit()

结果预览

word分割后子文件

对应转换为Excel的结果

对应转换为Excel的结果

在这里插入图片描述

目录页超链接示例

点击超链接即可跳转到指定的test case
在这里插入图片描述

对应sheet 超链接示例,点击超链接即可返回目录页
在这里插入图片描述

版权所有,禁止转载,禁止抄袭

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Python 是一种强大的编程语言,它具有处理文本、数据和自然语言的功能。Python 可以轻松地读取和提取各种数据,包括 Word 文档表格。 在 Python 批量提取 Word 表格,需要使用第三方库 python-docx。该库是专门用于读取和写入 Microsoft Word 文档的 Python 库。使用该库可以轻松地批量处理 Word 文档表格提取表格数据、插入数据或修改数据。 具体步骤如下: 1. 安装 python-docx 库。可以通过 pip install python-docx 命令来安装。 2. 使用 docx.Document() 创建一个 Word 文档对象。 3. 遍历文档表格,使用 table.rows 和 table.columns 获取表格的行列数。 4. 遍历表格的每一行,使用 row.cells 获取每一行的单元格。 5. 获取每一行单元格的数据,使用 cell.text 获取单元格的文本内容。 完整代码示例如下: ``` import docx # 创建一个 Word 文档对象 document = docx.Document('example.docx') # 遍历文档表格 for table in document.tables: # 获取表格的行列数 nrows = len(table.rows) ncols = len(table.columns) # 遍历每一行 for i in range(nrows): # 获取每一行的单元格 row_cells = table.rows[i].cells # 遍历每一行的单元格 for j in range(ncols): # 获取单元格数据 cell_data = row_cells[j].text print(cell_data) ``` 以上是使用 Python 批量提取 Word 表格的方法,该方法可以方便快捷地处理多个文档表格数据,提高数据处理效率。 ### 回答2: Python是一个非常强大的编程语言,能够处理各种数据类型和文件格式。针对word表格的批量提取,也可以使用Python轻松实现。以下是实现的方法: 1. 安装python-docx库 python-docx是Python的一个库,可以用于处理Word文档。首先需要在电脑上安装Pythonpython-docx库。 在命令行输入以下命令安装python-docx库: ``` pip install python-docx ``` 2. 打开word文档 使用Python打开word文档的代码如下所示: ```python import docx doc = docx.Document('file.docx') # ‘file.docx’是需要提取表格Word文档 ``` 3. 获取文档表格 使用以下代码可以获取文档的所有表格: ```python tables = doc.tables ``` 4. 批量读取表格数据 使用以下代码可以读取表格的数据: ```python for table in tables: for row in table.rows: for cell in row.cells: print(cell.text) ``` 以上代码将输出所有表格的行和列对应的文字内容。需要注意的是,表格可能包含合并的单元格,需要额外处理,可以使用python-docx库的Table类方法来处理。 5. 批量写入数据 批量写入数据时,可以先将表格转换为一个嵌套列表,然后将表格数据写入CSV文件。以下是转换表格并写入CSV文件的代码: ```python import csv import docx doc = docx.Document('file.docx') # ‘file.docx’是需要提取表格Word文档 tables = doc.tables for table in tables: data = [] keys = None for i, row in enumerate(table.rows): text = (cell.text for cell in row.cells) if i == 0: keys = tuple(text) continue row_data = dict(zip(keys, text)) data.append(row_data) with open(f"{table.name}.csv", "w", newline="") as f: writer = csv.DictWriter(f, keys) writer.writeheader() writer.writerows(data) ``` 以上代码将根据每个表格的名称自动将表格数据写入对应的CSV文件Python提取word表格的过程就是这样,简单易懂,实现起来也相对简单。而且Python操作word文档不需要Microsoft Office软件本身,只需要安装相应的库即可,操作更加便捷。 ### 回答3: 要批量提取 Word 表格,可以使用 Python python-docx 库。该库是一个用于创建、修改和提取 Microsoft Word 文档的Python解析程序库,并且非常适合处理 Word 文档表格。 具体步骤如下: 1. 安装 python-docx 库:可以通过 pip 命令进行安装,命令为 pip install python-docx。 2. 导入 python-docx 库:在 Python 代码导入 python-docx 库,以便使用库的函数和类。 3. 打开 Word 文档:使用库的 Document 类打开 Word 文档,方法为 doc = Document('filename.docx'),其 filename.docx 是要打开的 Word 文件名。 4. 遍历 Word 文档表格:使用 doc.tables 属性可以获取文档的所有表格,该属性返回一个表格列表,其每个表格都是一个 Table 对象。 5. 处理每一个表格:对于每个 Table 对象,使用 for 循环遍历每一行(row)和每一列(cell)以处理表格的数据。例如,要获取表格第2行第3列单元格的文本内容,并将其存储在变量 text ,可以使用 text = table.cell(2, 3).text。 6. 将数据保存到文件:使用 Python 的标准文件处理方法将数据保存到文件。 以下是一个示例代码,用于从一个 Word 文档提取所有表格的内容并将其保存到 CSV 文件: ```python import csv from docx import Document document = Document('example.docx') tables = document.tables for table in tables: with open('table.csv', 'a', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',') for row in table.rows: row_data = [] for cell in row.cells: row_data.append(cell.text) writer.writerow(row_data) writer.writerow([]) ``` 在此示例,我们使用 csv 库将提取到的表格内容保存到一个名为 table.csv 的 CSV 文件。我们使用 for 循环遍历每个表格,然后使用嵌套的 for 循环遍历每个行和单元格,提取单元格的文本内容,并将每一行的数据写入 CSV 文件。最后,我们在 CSV 文件插入一个空行以区分不同的表格。 总之,使用 python-docx 库可以方便地批量提取 Word 表格数据,提取的数据可以方便地保存到 CSV、JSON 或其他常见的数据格式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值