【Python】把excel中的内容批量替换到word中

安装依赖 xlrd PySimpleGUI python-docx openpyxl
 
pip3 install xlrd PySimpleGUI python-docx openpyxl

说明 ****** 1.excel不能合并单元格
2.excel表头必须是第一行,并且不能为空
3.excel中第一列的数据作为导出word文件名,或者在表头定义【文件名】列
4.word只支持.docx,excel只支持.xls/.xlsx 格式
5.word模板占位符格式:{xxx},xxx:是excel中表头列的名字

# -*- coding:utf-8 -*-
import os
 
import openpyxl
import PySimpleGUI as sg
import xlrd
from docx import Document
import time
import threading
 
"""
安装依赖 xlrd PySimpleGUI python-docx openpyxl
"""
 
 
def get_headers_from_xlsx(worksheet):
    headers = []
    for column in worksheet.iter_cols(1, worksheet.max_column):
        if column[0].value and len(str(column[0].value).strip()) > 0:
            headers.append(str(column[0].value).strip())
        else:
            headers.append('')
    return headers
 
 
def get_headers_from_xls(worksheet):
    num_cols = worksheet.ncols
    headers = []
    for curr_col in range(num_cols):
        cell = worksheet.cell_value(0, curr_col)
        if cell and len(str(cell).strip()) > 0:
            headers.append(str(cell).strip())
        else:
            headers.append('')
    return headers
 
 
def read_data_form_xlsx_file(excelFile):
    # 打开一个workbook
    try:
        workbook = openpyxl.load_workbook(excelFile)
    except FileNotFoundError as e:
        # print(e)
        print(f"当前文件不存在【{excelFile}】,程序自动退出!\n")
        return ([], [])
    except Exception as e:
        print(e)
        print(f"打开当前文件报错【{excelFile}】,程序自动退出!\n")
        return ([], [])
 
    # 抓取所有sheet页的名称
    sheetnames = workbook.sheetnames
    worksheets = workbook.worksheets
    print(f"读取表名为:{sheetnames}")
    if not worksheets or len(worksheets) == 0:
        return ([], [])
    worksheet = worksheets[0]
    # 遍历sheet1中所有行row
    num_rows = worksheet.max_row
    num_cols = worksheet.max_column
    print(f"当前excel共 {num_rows}行 {num_cols}列")
    if num_rows <= 0 or num_cols <= 0:
        return ([], [])
    headers = get_headers_from_xlsx(worksheet)
    print(f"开始读取表头:")
    if len(headers) == 0:
        return ([], [])
    print(f"{headers}\n")
    orderExpDatas = []
    # 遍历sheet1中所有单元格cell 第一行是标题
    for row in worksheet.iter_rows(2, worksheet.max_row):
        orderExpData = {}
        for coln in range(worksheet.max_column):
            if len(headers[coln]) > 0:
                cell = row[coln]
                if not cell or not cell.value or str(cell.value).strip() == "":
                    # print(f"第 {rown}行 {coln}列的 {headers[coln]} 为空")
                    orderExpData[headers[coln]] = ""
                else:
                    orderExpData[headers[coln]] = str(cell.value).strip()
        orderExpDatas.append(orderExpData)
    return (headers, orderExpDatas)
 
 
def read_data_form_xls_file(excelFile):
    # 打开一个workbook
    try:
        workbook = xlrd.open_workbook(excelFile)
    except FileNotFoundError as e:
        # print(e)
        print(f"当前文件不存在【{excelFile}】,程序自动退出!\n")
        return ([], [])
    except Exception as e:
        print(e)
        print(f"打开当前文件报错【{excelFile}】,程序自动退出!\n")
        return ([], [])
 
    # 抓取所有sheet页的名称
    sheetnames = workbook.sheet_names()
    worksheets = workbook.sheets()
    print(f"读取表名为:{sheetnames}")
    if not worksheets or len(worksheets) == 0:
        return ([], [])
    worksheet = worksheets[0]
    # 遍历sheet1中所有行row
    num_rows = worksheet.nrows
    num_cols = worksheet.ncols
    print(f"当前excel共 {num_rows}行 {num_cols}列")
    if num_rows <= 0 or num_cols <= 0:
        return ([], [])
    headers = get_headers_from_xls(worksheet)
    print(f"开始读取表头:")
    if len(headers) == 0:
        return ([], [])
    print(f"{headers}\n")
    orderExpDatas = []
    # 遍历sheet1中所有单元格cell
    for rown in range(num_rows):
        if rown == 0:
            continue
        orderExpData = {}
        for coln in range(len(headers)):
            if len(headers[coln]) > 0:
                cell = worksheet.cell_value(rown, coln)
                if not cell or str(cell).strip() == "":
                    # print(f"第 {rown}行 {coln}列的 {headers[coln]} 为空")
                    orderExpData[headers[coln]] = ""
                else:
                    orderExpData[headers[coln]] = str(cell).strip()
        orderExpDatas.append(orderExpData)
    return (headers, orderExpDatas)
 
 
def read_data_form_file(excelFile):
    isXlsx = excelFile.strip().lower().endswith('xlsx')
    return read_data_form_xlsx_file(excelFile) if isXlsx else read_data_form_xls_file(excelFile)
 
 
def replace_txt_by_data(text, restr, header_keys, header_datas):
    if text:
        for key in header_keys:
            if len(key) <= 0:
                continue
            regTxt = restr % (key)
            text = text.replace(regTxt, header_datas[key])
    return text
 
 
def replace_all_tables(tables, restr, header_keys, header_datas):
    if not tables or len(tables) == 0:
        return
    for tb in tables:
        for row in tb.rows:
            for cell in row.cells:
                if cell.text:
                    cell.text = replace_txt_by_data(cell.text, restr, header_keys, header_datas)
 
 
def write_word_file_from_datas(word_file, header_keys, header_datas, output_path):
    file_name = header_datas[header_keys[0]] if header_keys[0] else None
    default_file_name_key = '文件名'
    output_path_file = None
    if default_file_name_key in header_datas and len(header_datas[default_file_name_key].strip()) > 0:
        file_name = header_datas[default_file_name_key].strip()
    if file_name and len(file_name) > 0:
        doc = Document(word_file)
        restr = '{%s}'
 
        replace_all_tables(doc.tables, restr, header_keys, header_datas)
        for p in doc.paragraphs:
            if p and p.text:
                p.text = replace_txt_by_data(p.text, restr, header_keys, header_datas)
        if not os.path.exists(output_path):
            os.makedirs(output_path, exist_ok=True)
        output_path_file = os.path.join(output_path, '%s.docx' % file_name)
        doc.save(output_path_file)
        print('生成文件:%s' % output_path_file)
    else:
        print('文件名为空,跳过当前行')
    return output_path_file
 
 
def start_to_convert(word_file, excel_file, output_path):
    excel_headers, excel_datas = read_data_form_file(excel_file)
    if not excel_headers or len(excel_headers) == 0:
        print("excel表头信息为空,程序自动退出\n")
        return
    if not excel_datas or len(excel_datas) == 0:
        print("excel数据为空,程序自动退出\n")
        return
 
    time_str = time.strftime("%d-%H-%M-%S")
    output_path = os.path.join(output_path, 'docx', time_str)
    # os.makedirs(output_path, exist_ok=True)
    output_path_file_set = set()
    for data in excel_datas:
        output_path_file = write_word_file_from_datas(word_file, excel_headers, data, output_path)
        if output_path_file:
            output_path_file_set.add(output_path_file)
    print("共生成 %s 个文件" % (len(output_path_file_set)))
    print("\n")
 
 
class MyThread(threading.Thread):
    def __init__(self, word_file, excel_file, output_path):
        super(MyThread, self).__init__()  # 重构run函数必须要写
        self.daemon = True
        self.word_file = word_file
        self.excel_file = excel_file
        self.output_path = output_path
        self.running = False
        self.complete = False
        self.__lock = threading.Lock()
 
    def run(self):
        try:
            with self.__lock:
                if not self.running:
                    self.running = True
                    start_to_convert(self.word_file, self.excel_file, self.output_path)
                    self.running = False
                    self.complete = True
        except Exception as e:
            self.running = False
            self.complete = True
            print(e)
            print(f"执行失败:\n")
 
    def get_running(self):
        return self.running
 
    def get_complete(self):
        return self.complete
 
 
def main_launcher():
    sg.ChangeLookAndFeel('LightGreen')
 
    layout = [
        [sg.In(key='_wordfile_', size=(75, 1), enable_events=True, readonly=True), sg.FileBrowse("选择Word模板", font='Courier 12', size=(13, 1), file_types=(("Word File", "*.docx"),))],
        [sg.In(key='_excelfile_', size=(75, 1), enable_events=True, readonly=True), sg.FileBrowse("选择Excel", font='Courier 12', size=(13, 1), file_types=(("Excel File", "*.xls"), ("Excel File", "*.xlsx"),))],
        [sg.In(key='_outputfile_', size=(75, 1), enable_events=True, readonly=True), sg.FolderBrowse("输出Word目录", font='Courier 12', size=(13, 1))],
        [sg.Frame('日 志', font='Any 15',layout= [[sg.Output(size=(85, 35), font='Courier 12')]])],
        [sg.ReadFormButton('执 行', font='Courier 12', key='_start_', bind_return_key=True),
         sg.SimpleButton('退 出', font='Courier 12', key='_quit_', button_color=('white','firebrick3')),
         sg.T('excel不能合并单元格,以及表头必须是第一行【.docx/.xls/.xlsx】', size=(60, 1), font='Courier 12', text_color='red', justification='left')]
    ]
 
    window = sg.Window('小小程序',
                       auto_size_text=False,
                       auto_size_buttons=False,
                       default_element_size=(20,1,),
                       text_justification='right'
                       )
 
    window.Layout(layout)
    print_one = False
    mythread = None
 
    # ---===--- Loop taking in user input --- #
    while True:
        (button, values) = window.Read(timeout=100)
        if not print_one:
            print_one = True
            print('****************** 说明 ******************')
            print('1.excel不能合并单元格')
            print('2.excel表头必须是第一行,并且不能为空')
            print('3.excel中第一列的数据作为导出word文件名,或者在表头定义【文件名】列')
            print('4.word只支持.docx,excel只支持.xls/.xlsx 格式')
            print('5.word模板占位符格式:{xxx},xxx:是excel中表头列的名字')
            print('\n\n\n\n')
 
        if button in ('_quit_', None):
            break           # exit button clicked
 
        word_file = values['_wordfile_']
        excel_file = values['_excelfile_']
        output_path = values['_outputfile_']
 
        def check_word():
            if not word_file or len(str(word_file).strip()) == 0 or not str(word_file).strip().lower().endswith(".docx"):
                window['_wordfile_'].update(value='')
                sg.PopupAutoClose("请选择以【.docx】后缀的文件", title="", no_titlebar=False, modal=False, font='Courier 12')
                return False
            return True
 
        def check_excel():
            if not excel_file or len(str(excel_file).strip()) == 0 or not (str(excel_file).strip().lower().endswith(".xls") or str(excel_file).strip().lower().endswith(".xlsx")):
                window['_excelfile_'].update(value='')
                sg.PopupAutoClose("请选择以【.xls|.xlsx】后缀的文件", title="", no_titlebar=False, modal=False, font='Courier 12')
                return False
            return True
 
        def check_out_path():
            if not output_path:
                sg.PopupAutoClose("请选择输出目录", title="", no_titlebar=False, modal=False, font='Courier 12')
                return False
            return True
 
        if button == '_wordfile_':
            if not check_word():
                continue
        if button == '_excelfile_':
            if not check_excel():
                continue
 
        start_btn = '_start_'
        if button == start_btn:
            window[start_btn].update(disabled=True)
            if not check_word() or not check_excel() or not check_out_path():
                window[start_btn].update(disabled=False)
                continue
            print("******** 开始执行 ********")
            try:
                if not mythread:
                    mythread = MyThread(word_file, excel_file, output_path)
                    mythread.start()
                else:
                    if not mythread.get_complete():
                        if mythread.get_running():
                            print("程序运行中")
                        else:
                            mythread.start()
                    else:
                        mythread = MyThread(word_file, excel_file, output_path)
                        mythread.start()
            except Exception as e:
                print(f"执行失败:")
                print(e)
                window[start_btn].update(disabled=False)
        try:
            if mythread and mythread.get_complete():
                mythread = None
                print("******** 执行完成 ********\n\n\n\n\n")
                window[start_btn].update(disabled=False)
        except Exception as e:
            print(f"执行失败:")
            print(e)
            window[start_btn].update(disabled=False)
 
 
if __name__ == '__main__':
    main_launcher()

动态分析技术中最重要的工具是调试器,分为用户模式和内核模式两种类型。
用户模式调试器: 用来调试用户模式应用程序的调试器,工作在Ring3级,例如OllyDbg、x64dbg。也存在有VisualC++等编译器自带的调试器。
内核模式调试器: 能调试操作系统内核的调试器,例如WinDbg。

OllyDbg
简称OD,用户级调试器。OD是一款十分强大的32位调试器,虽然作者早已停止更新,但作为学习工具还是可以的。实践中建议使用x64dbg的32位版本。

64位平台可以使用x64dbg、IDA Pro等

操作窗口


配置
ollydbg.ini: OD中Options菜单里的所有设置,保存在ollydbg.ini中
UDD文件: OD的工程文件,用于保存当前调试的一些状态,例如断点、注释等,以便下次调试时继续使用。
插件: OD支持插件,并提供了相关api。这使得其扩展性非常好。

调试设置
单击“Options” —“Debugsingoplions” 选项,打开调试设置选项对话框,一般保持默认即可。其中,“Exceptions” (异常)选项用于设置让OllyDbg 忽路或不忽略某些异常,建议全部选择。 有关异常的知识将在第8 章讲解。

加载符号文件
点击“Debug”- "Seleet importlibraries” 选项,打开导入库窗口进行加载。符号文件可以在调试过程中,让函数以函数名字表示。比如存在一个函数叫“abc”,但是在调试器中很可能是一串数字”004012f7“,而符号文件就能让它显示为”abc“。

基本操作
常见快捷键:

断点
常用断点有:INT3断点、硬件断点、消息断点。

INT3断点
当你使用bp命令或者按下F2时,所设的断点为INT3断点。原理是把下断点的地方的指令换成CC指令来引发异常,调试器捕获异常后中断,并把原指令还原。例如:

 复制代码 隐藏代码
004011F3 68 D0404000
||||||||||||
004011F3 CC D0404000
优点: 可以设置无数个INT3断点,只要电脑受得住。
缺点: 由于修改了程序,所以易被检测

硬件断点
设断方法是在指定的代码行单击右键,执行快捷菜单中的“Breakpoint” 一 "Hardware,on execution”(“断点〞一,“硬件执行”)命令(也可以在命令行中设置“HE地址”)
最多可以设置四个硬件断点。

其中DR0、DR1、DR2、DR3可以设定地址,用来断下,DR7用来控制状态。
原理: 设置好断点后,运行到改地址时,CPU会向调试器发送异常,调试器捕获后中断。
优点: 执行速度快,不易被检测
缺点: 只能设置四个

你可能感兴趣的:(excel,word,python)