You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenPyXL批量读取Excel复制数据失败:输出表为空求助

Excel数据提取程序输出为空排查

我编写了基于OpenPyXL的Python程序,用于遍历指定文件夹中的Excel文件,提取特定x、y表头对应的数据并复制到输出工作表中。已确认源表存在目标数据,调试显示程序可正常读取文件及工作表,但输出表始终为空,恳请协助排查原因。

原代码

from openpyxl import load_workbook, Workbook
import os
from datetime import datetime
from tqdm import tqdm

# 设置输入输出路径
scan_folder_path = os.path.expanduser("c:\Users\n.margot\Desktop\scan")
output_file_path = os.path.expanduser("c:\Users\n.margot\Desktop\m2m.xlsx")

# 定义需要提取的表头映射
headings = {
    "Brent Swaps": [datetime(2023, 6, 1), datetime(2023, 7, 1), datetime(2023, 8, 1), datetime(2023, 9, 1), datetime(2023, 10, 1), datetime(2023, 11, 1), datetime(2023, 12, 1)],
    "EBOB": [datetime(2023, 4, 1), datetime(2023, 7, 1), datetime(2023, 8, 1), datetime(2023, 9, 1)],
    "Crack": [datetime(2023, 7, 1), datetime(2023, 8, 1), datetime(2023, 9, 1), datetime(2024, 1, 1)],
    "F/P": [datetime(2023, 7, 1), datetime(2023, 8, 1), datetime(2023, 9, 1)],
}

# 格式化日期为年月字符串(这里是问题根源之一)
for key in headings:
    headings[key] = [date.strftime("%Y-%m") for date in headings[key]]

# 加载输出工作簿
if os.path.exists(output_file_path):
    output_wb = load_workbook(output_file_path)
else:
    output_wb = Workbook()

output_sheet = output_wb.active

# 遍历文件夹中的文件
for file_name in tqdm(os.listdir(scan_folder_path), desc="Processing Files"):
    if file_name.endswith(".xlsx"):
        input_wb = load_workbook(os.path.join(scan_folder_path, file_name))
        # 遍历工作簿中的工作表
        for sheet_name in input_wb.sheetnames:
            print(f"Processing sheet {sheet_name} in file {file_name}")
            input_sheet = input_wb[sheet_name]
            # 遍历需要提取的表头对
            for x_heading, y_headings in headings.items():
                x_col = None
                y_cols = []
                # 查找x表头和匹配的y表头列
                for col in range(1, input_sheet.max_column + 1):
                    cell = input_sheet.cell(row=1, column=col)
                    if cell.value == x_heading:
                        x_col = col
                    elif cell.value:
                        try:
                            date_val = datetime.strptime(str(cell.value), '%d/%m/%Y').date()
                            # 这里用date对象和字符串列表匹配,永远不会成功
                            if date_val.replace(day=1) in y_headings:
                                y_cols.append(col)
                        except ValueError:
                            pass

                # 如果找到对应表头,复制数据
                if x_col is not None and y_cols:
                    print(f"Copying data from {sheet_name} ({x_heading})...")
                    for row in range(2, input_sheet.max_row + 1):
                        x_value = input_sheet.cell(row=row, column=x_col).value
                        if x_value is not None:
                            # 输出行号计算逻辑
                            output_row = len(output_sheet['A']) + 1
                            output_sheet.cell(row=output_row, column=1).value = sheet_name
                            output_sheet.cell(row=output_row, column=2).value = x_heading
                            output_sheet.cell(row=output_row, column=3).value = x_value
                            for y_col in y_cols:
                                y_value = input_sheet.cell(row=row, column=y_col).value
                                if y_value is not None:
                                    y_heading_val = input_sheet.cell(row=1, column=y_col).value
                                    # 多个y_col会覆盖同一行的4、5列,导致数据丢失
                                    output_sheet.cell(row=output_row, column=4).value = y_heading_val
                                    output_sheet.cell(row=output_row, column=5).value = y_value

# 保存输出工作簿
output_wb.save(output_file_path)
print(f"Data has been copied to {output_file_path}")

调试信息

C:\Users\n.margot\PycharmProjects\pythonProject\venv\Scripts\python.exe C:\Users\n.margot\PycharmProjects\pythonProject\main.py 
Processing Files:   0%|          | 0/4 [00:00<?, ?it/s]Processing sheet Sheet1 in file Curves 240323.xlsx
Processing sheet Sheet2 in file Curves 240323.xlsx
Processing sheet Sheet3 in file Curves 240323.xlsx
Processing sheet Sheet4 in file Curves 240323.xlsx
Processing sheet Sheet5 in file Curves 240323.xlsx
Processing sheet Sheet6 in file Curves 240323.xlsx
Processing sheet Market in file The COB-Advanced 20230324.xlsx
Processing sheet Rollover in file The COB-Advanced 20230324.xlsx
Processing Files: 100%|██████████| 4/4 [00:00<00:00, 26.74it/s]
Data has been copied to c:\Users\n.margot\Desktop\m2m.xlsx

Process finished with exit code 0

问题排查与修复

核心问题:日期类型不匹配

代码中先把headings里的日期转成了%Y-%m格式的字符串(比如"2023-07"),但后面匹配时,是将单元格日期转为date对象并执行replace(day=1),然后判断这个date对象是否在y_headings的字符串列表中,类型完全不匹配,导致永远无法找到匹配的y表头列,自然不会写入任何数据。

次要问题

  1. 路径转义问题:Windows路径中的反斜杠需要用双反斜杠或原始字符串,否则可能被解析为转义字符导致路径错误。
  2. 多y列数据覆盖:处理多个y列时,所有数据会写入同一行的第4、5列,导致后面的y值覆盖前面的,丢失数据。

修复后的代码

from openpyxl import load_workbook, Workbook
import os
from datetime import datetime, date
from tqdm import tqdm

# 使用原始字符串避免路径转义问题
scan_folder_path = os.path.expanduser(r"c:\Users\n.margot\Desktop\scan")
output_file_path = os.path.expanduser(r"c:\Users\n.margot\Desktop\m2m.xlsx")

# 将headings中的日期转为date对象(保留当月第一天),而非字符串
headings = {
    "Brent Swaps": [date(2023, 6, 1), date(2023, 7, 1), date(2023, 8, 1), date(2023, 9, 1), date(2023, 10, 1), date(2023, 11, 1), date(2023, 12, 1)],
    "EBOB": [date(2023, 4, 1), date(2023, 7, 1), date(2023, 8, 1), date(2023, 9, 1)],
    "Crack": [date(2023, 7, 1), date(2023, 8, 1), date(2023, 9, 1), date(2024, 1, 1)],
    "F/P": [date(2023, 7, 1), date(2023, 8, 1), date(2023, 9, 1)],
}

# 加载输出工作簿
if os.path.exists(output_file_path):
    output_wb = load_workbook(output_file_path)
    output_sheet = output_wb.active
else:
    output_wb = Workbook()
    # 新建工作簿时添加表头(可选,方便查看)
    output_sheet = output_wb.active
    output_sheet.append(["工作表名", "X表头", "X值", "Y表头", "Y值"])

# 遍历文件夹中的文件
for file_name in tqdm(os.listdir(scan_folder_path), desc="处理文件"):
    if file_name.endswith(".xlsx"):
        # data_only=True读取单元格实际值(而非公式)
        input_wb = load_workbook(os.path.join(scan_folder_path, file_name), data_only=True)
        # 遍历工作簿中的工作表
        for sheet_name in input_wb.sheetnames:
            print(f"处理工作表 {sheet_name}(文件:{file_name})")
            input_sheet = input_wb[sheet_name]
            # 遍历需要提取的表头对
            for x_heading, y_target_dates in headings.items():
                x_col = None
                y_cols = []
                # 查找x表头和匹配的y表头列
                for col in range(1, input_sheet.max_column + 1):
                    cell_val = input_sheet.cell(row=1, column=col).value
                    if cell_val == x_heading:
                        x_col = col
                    elif cell_val:
                        try:
                            # 适配不同的日期格式,如果源文件日期是datetime类型直接转date
                            if isinstance(cell_val, datetime):
                                cell_date = cell_val.date()
                            else:
                                cell_date = datetime.strptime(str(cell_val), '%d/%m/%Y').date()
                            # 转为当月第一天,和目标日期匹配
                            normalized_date = cell_date.replace(day=1)
                            if normalized_date in y_target_dates:
                                y_cols.append(col)
                        except ValueError:
                            # 非日期格式跳过
                            continue

                # 如果找到对应表头,复制数据
                if x_col is not None and y_cols:
                    print(f"从 {sheet_name}({x_heading})复制数据...")
                    for row in range(2, input_sheet.max_row + 1):
                        x_value = input_sheet.cell(row=row, column=x_col).value
                        if x_value is None:
                            continue
                        # 每个y值对应一行,避免覆盖
                        for y_col in y_cols:
                            y_value = input_sheet.cell(row=row, column=y_col).value
                            if y_value is None:
                                continue
                            y_heading_val = input_sheet.cell(row=1, column=y_col).value
                            # 直接append一行数据,无需手动计算行号
                            output_sheet.append([sheet_name, x_heading, x_value, y_heading_val, y_value])

# 保存输出工作簿
output_wb.save(output_file_path)
print(f"数据已保存到 {output_file_path}")

修复说明

  1. 日期类型统一:将headings中的日期转为date对象,匹配时将单元格日期也转为date对象并统一为当月第一天,确保类型和值都能匹配。
  2. 路径修复:使用原始字符串r""定义路径,避免转义问题。
  3. 数据写入优化:使用output_sheet.append()直接添加行,无需手动计算行号,同时每个y值对应一行,避免数据覆盖。
  4. 添加表头:新建输出文件时自动添加表头,方便数据查看。
  5. data_only=True:加载输入工作簿时使用该参数,确保读取单元格的实际值(而非公式)。

内容的提问来源于stack exchange,提问作者probslater

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 00:48:08