使用Python轻松实现文档编写

大家好,本文将介绍如何使用Python轻松实现文档编写,减少报告撰写的痛苦,使用Microsoft Word、pythonpython-docx库来简化报告撰写和从报告中提取信息。

案例

  • 读取一个Word文档并进行编辑。

虽然听起来可能不那么令人振奋,但根据支持系统的不同,可以通过巧妙运用这个设置来大大减少制作报告所需的时间。代码将在下面分享和解释,但用户可以在GitHub上获取代码的基础版本。强烈建议根据特定用例修改代码,以适应具体使用情况。

开始

在使用pip3安装python-docx之后,建议将程序拆分为读取两种不同类型内容的部分,即段落和表格。

def tableSearch(doc):
    """ Takes doc file
Purpose: Scrapes all tables and pulls back text
Returns: Dictionary of tables
"""
    tables = {}
    table = 0
    x = 0
    y = 0

    while table != len(doc.tables):
        logging.debug("Table {} Row: {}".format(table, len(doc.tables[table].rows)))
        logging.debug("Table {} Column: {}".format(table, len(doc.tables[table].columns)))
        logging.debug("Table {}".format(table))
        table_test = doc.tables[table]
        run = doc.add_paragraph().add_run()
        font = run.font
        while x != len(table_test.rows):
            while y != len(table_test.columns):
                logging.debug("Table: {}, X: {}, Y: {}\n{}\n".format(table, x,y,table_test.cell(x,y).text))
                tables[str(table)+"."+str(x)+"."+str(y)] = {"row": x, "column": y, "text": table_test.cell(x,y).text}
                y += 1
            x += 1
            y = 0
        x = 0
        y = 0
        table += 1

    return tables


# 读取Word文档
def wordDocx(file):
    """ Takes file
Purpose: Reads the Word Doc
Returns: Nothing
"""
    logging.debug("File: {}".format(file))
    doc = docx.Document(file)
    fullText = []

    for para in doc.paragraphs:
        logging.debug("Paragraph: {}".format(para.text))
        fullText.append(para.text)

    tableInfo = tableSearch(doc)
    return [fullText, tableInfo]

段落

段落的解析非常简单。它们被设置为一个列表,可以以类似的方式访问(doc.paragraphs[n])。

如果想更改段落中的文本,只需使用下面的代码即可修改文本:

oldText = doc.paragraphs[indexOfParagraph].text

replacedText = oldText.replace("",clientLongName)

doc.paragraphs[indexOfParagraph].text = "{} + Text to add".format(oldText)
doc.paragraphs[indexOfParagraph].text = "{} + Replaced text".format(replacedText)

正如上文所述,可以通过制作一个包含关键词(如)的普通文档模板,然后使用类似这样的程序去更改每个关键词的所有实例。虽然这也可以通过打开文档并执行查找和替换操作来实现,但支持此操作的基础结构可以使手动更改的快速过程变得更快。

表格

表格要稍微复杂一些,原因在于表格是二维的,而段落是一维的(段落是0至n的,而表格有一个X坐标和一个Y坐标)。由于表格的处理方式不同,因此需要一种更复杂的方式来循环遍历所有表格,并索引其中的某些内容。

关于在表格中选择元素的另一个重要注意事项。虽然选择单元格需要使用xy值,但还需要选择要引用的表格。

table = 0
x = 0
y = 0

while table != len(doc.tables):
    table_test = doc.tables[table]
    while x != len(table_test.rows):
        while y != len(table_test.columns):
            logging.debug("Table: {}, X: {}, Y: {}\n{}\n".format(table, x,y,table_test.cell(x,y).text))
            y += 1
        x += 1
        y = 0
    x = 0
    y = 0
    table += 1

像这样对程序进行迭代有多个目的。

  1. 不论每个表格的结构如何,程序都会以相同方式循环遍历每个表格。

  2. 记录和显示单元格的位置可以进行有趣的集成。

一个有趣集成的例子是创建包含值及其位置的字典,以便插入信息。

虽然为此设计一个查询系统超出了本文的讨论范围,但本文已经提供了一个基本设置,可以帮助你入门:

ref = {
"Date": [0,2,12]
}

info = {
"Date": "08/24/2023"
}

for key,value in ref.items:
    if table == value[0] and x == value[1] and y == value[2]:
        table_test.cell(x,y).text = info[key]

扩展用途

使用与前面相同的循环系统,还可以让某个程序解析已完成报告中的信息,并将其导出为CSV文件,以便在其他报告撰写程序中使用。本文使用过并推荐的两个报告撰写平台是PlexTrac和AttackForge。这两个平台都允许通过一个特殊格式的CSV文件或JSON文件导入调查结果。

如何简化向其他报告/系统导入信息的过程:

在上面的代码中,每次循环遍历段落或表格中的一个段落或单元格时,都会有代码可以查看对象中的内容。这不仅可以用于调试,本文将文本添加到字典中。这样可以节省从中提取数据的时间,而将更多时间用于解析和重新格式化提取回来的数据。

# 段落
fullText = []
# 表格
tables = {}

fullText.append(para.text)
tables[str(table)+"."+str(x)+"."+str(y)] = {"row": x, "column": y, "text": table_test.cell(x,y).text}

分别添加这几行后,就可以抓取数据,并以类似于访问数据的格式保存数据。一旦获得了数据并知道在哪里可以找到它,就可以在各自的对象中循环遍历,并使用这些数据创建一个新文件。

举个简单的例子,本文将在GitHub上的代码中添加一个函数,将数据保存为CSV格式。

import docx
import logging
import argparse
import re
import sys

logging.basicConfig(level=logging.DEBUG)

# 设置日志记录
def get_arg():
    """ Takes nothing
Purpose: Gets arguments from command line
Returns: Argument's values
"""
    parser = argparse.ArgumentParser()
    # CLI 版本
    # parser.add_argument("-d","--debug",dest="debug",action="store_true",help="Turn on debugging",default=False)
    parser.add_argument("-d","--debug",dest="debug",action="store_false",help="Turn on debugging",default=True)
    # 文件版本
    parser.add_argument("-f","--file",dest="file", help="Name of the Word Doc.")
    parser.add_argument("-o","--output",dest="output", help="Name of the file to output the results.")
    options = parser.parse_args()

    if not options.output:
        options.output = "output.csv"
    if not options.file:
        logging.error("Please provide a file name.")
        sys.exit()

    if options.debug:
        logging.basicConfig(level=logging.DEBUG)
        global DEBUG
        DEBUG = True
    else:
        logging.basicConfig(level=logging.INFO)
    return options


def tableSearch(doc):
    """ Takes doc file
Purpose: Scrapes all tables and pulls back text
Returns: Dictionary of tables
"""
    tables = {}
    table = 0
    x = 0
    y = 0

    while table != len(doc.tables):
        logging.debug("Table {} Row: {}".format(table, len(doc.tables[table].rows)))
        logging.debug("Table {} Column: {}".format(table, len(doc.tables[table].columns)))
        logging.debug("Table {}".format(table))
        table_test = doc.tables[table]
        run = doc.add_paragraph().add_run()
        font = run.font
        while x != len(table_test.rows):
            while y != len(table_test.columns):
                logging.debug("Table: {}, X: {}, Y: {}\n{}\n".format(table, x,y,table_test.cell(x,y).text))
                tables[str(table)+"."+str(x)+"."+str(y)] = {"row": x, "column": y, "text": table_test.cell(x,y).text}
                y += 1
            x += 1
            y = 0
        x = 0
        y = 0
        table += 1

    return tables


# 读取word文档
def wordDocx(file):
    """ Takes file
Purpose: Reads the Word Doc
Returns: Nothing
"""
    logging.debug("File: {}".format(file))
    doc = docx.Document(file)
    fullText = []

    for para in doc.paragraphs:
        logging.debug("Paragraph: {}".format(para.text))
        fullText.append(para.text)

    tableInfo = tableSearch(doc)
    return [fullText, tableInfo]


def infoToCSV(para, table):
    """ Takes a list and a dictionary
Purpose: Convert dictionary and paragraph into a CSV file
Returns: Nothing
"""
    csvHeaders = "Name,Description,Recommendation\n"

    for key, values in table.items():
        Name = ""
        Description= ""
        Recommendation= ""
        nameRegex = re.search("0\.\d+\.1",key) 
        desRegex = re.search("0\.\d+\.3",key)
        recRegex = re.search("0\.\d+\.6",key)
        if nameRegex:
            Name = value["text"]
        if desRegex:
            Description = value["text"]
        if recRegex:
            Recommendation = value["text"]          
        csvHeaders += "{},{},{}\n".format(Name,Description,Recommendation)
    
    for item in para:
        values = re.findall("Name\":\"(.+?)\".+?Description\":\"(.+?)\".+?Recommendation\":\"(.+?)\"",item)
        try:
            csvHeaders += "{},{},{}\n".format(values[0],values[1],values[2])
        except IndexError:
            continue

    f = open("Output.csv", "w")
    f.write(csvHeaders)
    f.close()


def main():
    options = get_arg()
    logging.debug("Options: {}".format(options))
    info = wordDocx(options.file)
    logging.debug("Info: {}".format(info))
    infoToCSV(info[0], info[1])

if __name__ == '__main__':
    main()

需要根据尝试要导入信息的程序来修改infoToCSV函数。虽然这可能有点烦人,但在必须制作多份报告时,在这里花费的5-20分钟来使函数适应需求是值得的。根据本文的经验,手动重新输入这些信息很快就会变得非常枯燥。

局限性

在本文中,你可能已经注意到没有涉及图片的任何内容,这有一个令人遗憾的原因:python-docx无法处理图片文件,除非在添加图片时使用它,否则它似乎根本不会注意到图片文件。要解决这个问题,要么需要进入库的源代码并对其进行集成,要么需要解压缩.docx文件并解析图像标签的XML,然后从媒体文件夹中查找图片。

你可能感兴趣的:(python,开发语言)