用 Python 读 Excel:日期变 datetime、公式读成 None、空行多出一半

举报
yd_231995061 发表于 2026/11/12 00:53:09 2026/11/12
【摘要】 三份记账 Excel 要并成 CSV,用 openpyxl 读,五个坑挨个踩:日期读出来是 datetime 不是字符串;data_only=True 拿到 None,因为公式没被 Excel 算过;合并单元格只有左上角有值;给空单元格设个样式 max_row 就翻倍、多出五行空行;CSV 不带 BOM 用 Excel 打开全乱码。

三份记账 Excel,两年多的数据,想把它们并成一份 CSV。本来以为读单元格、拼一行、写文件,十几行代码的事。结果五个坑挨个踩了一遍,每一个都不报错,只是给你错的数据。

用的 openpyxl,装了就一个包。

坑一:日期读出来是 datetime,不是你想的字符串

单元格里明明写的是 2026-11-01,读出来是这样:

repr(ws["A2"].value)           →  datetime.datetime(2026, 11, 1, 0, 0)
type(ws["A2"].value).__name__  →  'datetime'

它不是字符串,也不是 date,是 datetime。直接写进 CSV 变成 2026-11-01 00:00:00,等你想按天分组的时候发现全带时间尾巴。

from datetime import datetime, date
v = ws["A2"].value
if isinstance(v, (datetime, date)):
    v = v.strftime("%Y-%m-%d")

坑二:data_only=True 读出来是 None

表里有一列合计是公式 =SUM(C2:C4)。想拿算好的值,用了 load_workbook(path, data_only=True),结果拿到 None。

原因是 openpyxl 自己不计算公式。data_only=True 只是去读 Excel 上次保存时缓存的结果。这个文件如果是脚本刚生成的、从没被 Excel 打开保存过,缓存就是空的,只能给你 None。

两种处理:用 Excel 手动打开保存一次;或者干脆别读公式列,自己算。

坑三:合并单元格只有左上角有值

B4:B5 合并写了「合并测试」,读出来是:

B4  →  '合并测试'
B5  →  None

遍历行的时候 B5 那行就少一个字段。如果合并的是「月份」这种关键列,按月份统计会直接漏掉一半数据。可以先展开再读:

for rng in ws.merged_cells.ranges:
    top = ws.cell(rng.min_row, rng.min_col).value
    for r in range(rng.min_row, rng.max_row + 1):
        for c in range(rng.min_col, rng.max_col + 1):
            ws.cell(r, c).value = top

坑四:给空单元格设个样式,max_row 就翻倍

这个最阴。我在 A12 设了加粗,一个字都没写。存盘再读:

max_row = 12          ← 实际数据只到第 7 行
iter_rows 行数 = 12
最后三行 = [(None, None, None, None), (None, None, None, None), (None, None, None, None)]

7 行数据读出 12 行,多出 5 行全 None。因为 max_row 统计的是「有内容或有格式」的最大行,一个字体设置也算数。写到 CSV 就是一堆空行。

别拿 max_row 当行数,过滤一遍:

rows = [r for r in ws.iter_rows(values_only=True)
        if any(c is not None and str(c).strip() != "" for c in r)]

坑五:CSV 不带 BOM,Excel 打开全乱码

Python 默认 encoding="utf-8" 写出来的中文 CSV,双击用 Excel 打开是乱码。看一眼文件头就明白:

utf-8      →  b'.\xe6\x97\xa5\xe6\x9c\x9f,'
utf-8-sig  →  b'\xef\xbb\xbf.\xe6\x97\xa5\xe6\x9c\x9f,'

后者多出 EF BB BF 三个字节,就是 BOM,Excel 认这个。

with open("out.csv", "w", newline="", encoding="utf-8-sig") as f:

newline="" 也别省,不然 Windows 上每行会多一个空行。

合成一段能跑的代码

把上面五个坑都堵上,从读 Excel 到写 CSV 完整一段:

import openpyxl, csv
from datetime import datetime, date

wb = openpyxl.load_workbook("bill.xlsx", data_only=False)
ws = wb.active

# 展开合并单元格
for rng in ws.merged_cells.ranges:
    top = ws.cell(rng.min_row, rng.min_col).value
    for r in range(rng.min_row, rng.max_row + 1):
        for c in range(rng.min_col, rng.max_col + 1):
            ws.cell(r, c).value = top

def clean(v):
    if isinstance(v, (datetime, date)):
        return v.strftime("%Y-%m-%d")
    return v

with open("bill.csv", "w", newline="", encoding="utf-8-sig") as f:
    w = csv.writer(f)
    for row in ws.iter_rows(values_only=True):
        if not any(c is not None and str(c).strip() != "" for c in row):
            continue
        w.writerow([clean(c) for c in row])

十几份表、二十多万行,这套跑下来没再出问题。真正花时间的不是写代码,是发现「读出来的东西和你眼睛看到的不是一回事」。这五个坑没有一个会抛异常,全都安安静静给你错的数据,直到你拿去做统计才发现对不上。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。