做数据相关工作的人,大概率都干过这样一件事:把数据库里的数据导出来,整理成 Excel 发给业务部门。小数据量还好说,几万行以内用数据库客户端手动导一下就行。可一旦数据量上来了,或者需要每周每月重复做同样的事,手动操作就变成一种折磨。我最早是在某公司做数据报表的时候被这个场景逼得没办法,才开始认真研究用 Python 批量导出数据库数据至 Excel 文件这件事。写完之后发现,这东西不仅能解放双手,还能顺手解决格式统一、定时刷新、多人协作这些麻烦事。
这篇文章就把我实际使用的一套方案,从选型到踩坑完整写出来。适合正在被各种导出需求困扰的数据分析师、后台开发、运维同学,也适合刚学 Python 想找一个练手项目的人。全文的代码我都在本地环境里验证过,如果你手里的数据库是 MySQL、PostgreSQL、SQLite 这些常见类型,基本可以直接拿来改改用。导出结果统一按“每张表一个文件”或“每张表一个工作表”两种方式演示,覆盖大部分业务需求。
1. 需求拆解与技术选型
1.1 批量导出这件事,到底难在哪
很多人第一反应是:不就是SELECT * FROM 表名然后另存为 CSV 吗?确实,数据量小的时候,Navicat 这类客户端自带导出功能,几秒钟就完事。但一旦进入“批量”这个场景,问题就开始冒头了。
第一个痛点是表数量多。一个业务库几十张表,手动一张张导,光点鼠标就要半天。第二个痛点是周期性重复,比如每个月月初要给运营部门导出上个月的数据,操作完全一样,但每次都要重来一遍。第三个痛点是格式要求,业务方经常要求表头加颜色、列宽自适应、数值保留两位小数,这些手动操作在 Excel 里处理起来机械又耗时。第四个痛点是数据库类型不统一,有人用 MySQL,有人用 PostgreSQL,还有人用 SQLite 或 SQL Server,换一个库就要换一套工具。
批量导出的需求本质上可以拆成四件事:连接数据库、执行查询、数据转换、写入 Excel。这四个环节里,连接数据库和写入 Excel 是最容易出现环境差异的地方,而数据转换则是性能瓶颈的重灾区。想清楚这四个环节分别用什么工具,整个方案就清晰了。
1.2 技术选型:pandas + SQLAlchemy + openpyxl 的组合逻辑
我最终选择的技术栈是pandas + SQLAlchemy + openpyxl,这个组合看起来基础,但确实是最稳的。先说为什么用 SQLAlchemy。它的核心价值是统一的连接接口,不管后端是 MySQL、PostgreSQL 还是 SQLite,代码里只改一个连接字符串就行,业务逻辑完全不用动。这一点在维护多个数据库导出任务时特别省心。
再说 pandas。它负责中间的数据处理和转换,read_sql方法可以直接把 SQL 查询结果变成 DataFrame,to_excel方法又可以直接把 DataFrame 写进 Excel。这两个方法之间几乎不需要写额外代码,堪称效率利器。对于大多数不太复杂的导出需求,pandas 一套流程走下来,代码量可以控制在二十行以内。
最后是落盘工具 openpyxl。它负责把数据真正写进 Excel 文件,而且支持后期对工作表做样式调整,比如改字体、加边框、冻结首行。需要提一下的是,pandas 在调用to_excel时可以通过engine参数选择openpyxl还是xlsxwriter。简单说,xlsxwriter 写入速度更快,适合一次性导出超大文件;openpyxl 的后期格式化能力更强,适合导出之后再调整样式。我日常默认用 openpyxl,因为大多数场景导出后都免不了要调整格式。
不同的数据库连接ABCD动因差异:
MySQL: mysql+pymysql://用户名:密码@主机:端口/库名?charset=utf8mb4 PostgreSQL: postgresql+psycopg2://用户名:密码@主机:端口/库名 SQLite: sqlite:///本地文件路径 SQL Server: mssql+pyodbc://用户名:密码@主机:端口/库名?driver=ODBC+Driver+17+for+SQL+Server这套组合最大的好处是代码结构高度统一:连接字符串换个前缀,剩下所有流程共用一套逻辑。我在本地测试环境里用三个不同类型的库跑过,除了驱动版本需要注意,整体没有任何需要额外改的地方。
2. 环境准备与依赖配置
2.1 从零搭建一套可用的 Python 环境
这里我假定你已经装了 Python 3.8 以上的版本,并且能正常使用 pip。如果电脑上还没有 Python,去官网下载安装包时记得勾选“Add Python to PATH”,这个选项很多人会漏掉,漏掉之后在终端里执行python命令会提示找不到,非常影响心情。
建议新建一个虚拟环境来隔离依赖,尤其是你机器上同时存在多个项目的时候。我之前吃过亏,全局环境里装了一堆新旧不兼容的包,后来某个库升级直接导致另一个项目跑不起来。虚拟环境能把这个风险降到最低:
python -m venv export_env export_env\Scripts\activate # Windows source export_env/bin/activate # macOS / Linux激活之后,终端出现(export_env)前缀,这时安装的包会被隔离到这个环境里,不会污染全局。然后安装核心依赖:
pip install pandas sqlalchemy openpyxl pip install pymysql # 连 MySQL 用 pip install psycopg2-binary # 连 PostgreSQL 用如果你是连 SQLite,不需要额外装驱动,Python 内置的sqlite3标准库跟 SQLAlchemy 配合得很好。连 SQL Server 的话需要装 pyodbc,并且要确认系统里有对应的 ODBC Driver,这一步最容易出问题,很多人的报错都卡在驱动上。
2.2 数据库账号权限的准备工作
这一步看似不起眼,但实际中经常被忽略。导出任务需要一个数据库账号,这个账号要有查询表的权限。注意,批量导出几十张表的情况下,如果账号只有某几张表的权限,程序跑到一半就会报权限错误。建议在正式开写之前,先在数据库里执行一下全库表清单查询,用 SELECT 确认你账号能看到的表范围。
另外,连接字符串里的密码如果有特殊字符,比如@、#、:这些,注意要做 URL 编码,否则 SQLAlchemy 解析连接字符串时会报错。我遇到过一个问题:密码里带了一个@,结果 SQLAlchemy 把@当成了主机和端口之间的分隔符,连接直接失败。最后用urllib.parse.quote_plus对密码编码后解决。这个细节,网上很多教程都没提过,但是实际使用中太容易踩了。
from urllib.parse import quote_plus password = quote_plus("你的密码") engine = create_engine(f"mysql+pymysql://root:{password}@localhost:3306/mydb?charset=utf8mb4")还有一个小建议:导出的脚本里尽量不要明文写账号密码,尤其是脚本要提交到代码仓库或者要发给别人的时候。比较稳妥的做法是把连接信息放到环境变量或者一个单独配置文件中,用.gitignore把配置文件排除掉。我自己的项目里,通常用.env文件存数据库连接字符串,脚本运行前自动读取。
2.3 认识你要导出的数据:先看表结构再写代码
批量导出之前,强烈建议你先了解一下数据库里有哪些表、大概每张表多少行数据。这一步能避免很多后续问题。比如一张千万级的日志表,你要是没做任何限制直接全量导出,Excel 文件根本装不下,程序也会因为内存占用过高直接挂掉。
在 Python 中查看表清单很简单:
from sqlalchemy import create_engine, inspect engine = create_engine("mysql+pymysql://root:密码@localhost:3306/mydb?charset=utf8mb4") inspector = inspect(engine) tables = inspector.get_table_names() print(tables)对于大表,可以通过SELECT COUNT(*)估算数据量,再用SHOW CREATE TABLE或PRAGMA table_info看字段定义。这些信息能帮你提前判断哪些表需要分块导出,哪些表只需要导前几行,以及字段类型里有没有容易出问题的类型。
3. 核心代码实现与实操过程
3.1 单表导出:三行代码搞定基础需求
拿到单表导出的需求,最简方案确实只要三行代码。这里用 MySQL 举例:
import pandas as pd from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:密码@localhost:3306/mydb?charset=utf8mb4") df = pd.read_sql("SELECT * FROM user_info", engine) df.to_excel("user_info.xlsx", index=False)第一行创建数据库连接引擎,第二行执行查询并把结果读成 DataFrame,第三行把 DataFrame 写入 Excel。index=False是必须的,不然 pandas 默认会把行号也写进去,文件里多出一列没意义的0, 1, 2...,业务方看到肯定会困惑。
如果你不需要全表字段,只导部分列,SQL 里直接写列名即可。需要按条件过滤,就加 WHERE 子句。这种写法简洁明了,唯一的不足是没有做任何数据量控制,适合小表或者一次性任务。
我发现很多初学者会在这里卡一个点:to_excel之后的文件被 Excel 打开时会提示“文件格式和扩展名不匹配”。这种情况大概率是 pandas 版本和 openpyxl 版本不兼容导致的。解决方法是升级这两个库到最新版:
pip install --upgrade pandas openpyxl虽然听起来像万能回复,但这个问题确实是升级后就没再出现过了。
3.2 全库批量导出:两种常见的输出形态
真正的“批量导出”场景,通常有两种需求形态。一种是每张表导出一个独立的 Excel 文件,文件名用表名命名;另一种是全部表导到一个 Excel 文件里,每张表占用一个工作表。两种我都有对应的代码模板。
先看每张表一个 Excel 文件的写法:
import pandas as pd from sqlalchemy import create_engine, inspect engine = create_engine("mysql+pymysql://root:密码@localhost:3306/mydb?charset=utf8mb4") inspector = inspect(engine) tables = inspector.get_table_names() for table in tables: df = pd.read_sql(f"SELECT * FROM {table}", engine) df.to_excel(f"{table}.xlsx", index=False) print(f"表 {table} 已导出,共 {len(df)} 行")这个代码简单直观,但要注意几个坑。第一,表名如果包含特殊字符,直接拼进 SQL 有可能出问题,稳妥的做法是用 SQLAlchemy 的text方法做参数绑定,或者对表名使用反引号包裹。第二,有些表是视图,get_table_names默认只返回表,不返回视图,如果你的需求要包含视图,需要额外用get_view_names获取。第三,Excel 对文件名中的某些字符有限制,比如/、\、*等,如果表名本身带着这些字符,文件保存会失败。
再来看全部数据汇总到一个工作簿的写法:
import pandas as pd from sqlalchemy import create_engine, inspect engine = create_engine("mysql+pymysql://root:密码@localhost:3306/mydb?charset=utf8mb4") tables = inspect(engine).get_table_names() with pd.ExcelWriter("全库数据汇总.xlsx", engine="openpyxl") as writer: for table in tables: df = pd.read_sql(f"SELECT * FROM {table}", engine) df.to_excel(writer, sheet_name=table, index=False)这段代码用ExcelWriter上下文管理器把多个 DataFrame 写入同一个文件的多个工作表。需要注意 Excel 工作表名称有两个硬性限制:长度不能超过 31 个字符,不能包含: \ / ? * [ ]这些特殊字符。实际做的时候,我通常会对 sheet_name 做一次处理,截断或替换特殊字符:
def clean_sheet_name(name): invalid_chars = r'[]:*?/\\' for ch in invalid_chars: name = name.replace(ch, '_') return name[:31]3.3 大表导出性能优化:分块读取与按主键分页
如果某张表有上百万行数据,直接SELECT * FROM然后丢给 pandas,你会发现两个问题:一是内存占用飞到几个 GB,程序可能直接被系统杀掉;二是单次读取这么大的数据,数据库端和 Python 端都压力巨大。这种情况下必须分块读取。
pandas 的read_sql本身就支持chunksize参数,这是最简单的分块方案:
import pandas as pd from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://root:密码@localhost:3306/mydb?charset=utf8mb4") chunk_size = 20000 with pd.ExcelWriter("big_table.xlsx", engine="openpyxl") as writer: for chunk in pd.read_sql("SELECT * FROM big_log", engine, chunksize=chunk_size): chunk.to_excel(writer, sheet_name="数据", index=False, header=False)注意,第一块数据写入时要保留表头,后续块要设置header=False,否则每块都会重复写一遍表头。更严谨的写法是先判断是不是第一块:
first = True for chunk in pd.read_sql("SELECT * FROM big_log", engine, chunksize=chunk_size): if first: chunk.to_excel(writer, sheet_name="数据", index=False) first = False else: chunk.to_excel(writer, sheet_name="数据", index=False, header=False)chunksize的取值不是越大越好。我实测过,2 万到 5 万这个区间通常表现不错。太小会导致数据库查询次数过多,网络开销大;太大会导致单块数据处理时间过长,遇到内存不足的风险增加。具体数值还要根据你服务器内存和表字段的宽度去调整。
另一种更可控的分块方式是手动用主键做分页。如果你的表有一个自增主键id,比如最大值是 100 万,可以用每 5 万一个区间来查询:
start = 1 step = 50000 max_id = 1000000 while start <= max_id: end = start + step df = pd.read_sql(f"SELECT * FROM big_log WHERE id BETWEEN {start} AND {end}", engine) df.to_excel(f"big_log_{start}_{end}.xlsx", index=False) start = end + 1这种方法的好处是查询计划稳定,通过主键范围扫描,数据库不容易因为 OFFSET 越翻越慢。基于 OFFSET 的分页在数据量大时性能会急剧下降,因为数据库需要先扫描并丢弃前面所有行才能定位到目标位置。我之前导一张 80 万行的表,用 OFFSET 分页到后面每页要等几十秒,换成主键范围分页后,整个导出时间缩短了一大半。
3.4 导出后的样式处理:让 Excel 不再是“裸数据”
默认导出的 Excel 文件是这样的:表头没有加粗,行高列宽都是默认值,数据没有任何样式。业务方拿到这种文件虽然能看,但体验确实一般。如果你希望导出的文件表头醒目、列宽适中、甚至冻结首行,就需要在导出后对工作簿做一步后处理。
我的习惯是把“数据写入”和“样式调整”分成两个阶段。先用 pandas 导入数据,再用 openpyxl 加载并调整样式。这样代码的可维护性更好,数据导出逻辑和展示逻辑互不干扰。
import pandas as pd from openpyxl import load_workbook from openpyxl.styles import Font, PatternFill, Alignment from openpyxl.utils import get_column_letter # 第一步:写入数据 df = pd.read_sql("SELECT * FROM user_info", engine) df.to_excel("user_info_styled.xlsx", index=False) # 第二步:加载并修改样式 wb = load_workbook("user_info_styled.xlsx") ws = wb.active header_font = Font(bold=True, color="FFFFFF", size=11) header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid") center_alignment = Alignment(horizontal="center", vertical="center") for col in range(1, ws.max_column + 1): cell = ws.cell(row=1, column=col) cell.font = header_font cell.fill = header_fill cell.alignment = center_alignment # 自动列宽:根据每列内容长度调整 for col in range(1, ws.max_column + 1): max_len = 0 col_letter = get_column_letter(col) for cell in ws[col_letter]: if cell.value is not None: max_len = max(max_len, len(str(cell.value))) ws.column_dimensions[col_letter].width = min(max_len + 4, 50) # 冻结首行 ws.freeze_panes = "A2" wb.save("user_info_styled.xlsx")这段代码有几个值得注意的点。get_column_letter是把数字列号转成字母的工具函数,比如 1 转成 A、27 转成 AA,避免手动拼字母的尴尬。自动列宽的最大值我限制在 50,因为太宽的列在实际阅读时并不友好,而且某些超长文本字段会把列宽撑得很夸张。freeze_panes = "A2"表示冻结第一行,滚动时表头始终可见,这个功能业务方普遍反馈很好用。
如果你要把上面的样式处理合并到批量导出多个工作表的流程里,也可以在每个工作表的循环中分别调整。注意一下,pandas.ExcelWriter在写入完所有数据后,工作表对象还保存在工作簿里,你可以通过writer.book拿到工作簿,然后逐表处理。但这部分的代码复杂度会明显上升,我更推荐“写入完成后二次加载修改”的方案,逻辑更清晰,调试也方便。
4. 常见问题与排查技巧实录
4.1 中文乱码:源头在连接字符串
导出文件里中文显示成乱码,这个问题的出现频率超乎很多人想象。现象分两种:一种是 Excel 打开后中文全是问号,另一种是数据库里的中文在导出的 Excel 里变成了类似汉å-的乱码。
第一种“问号乱码”大概率是连接字符串里没指定字符集,或者指定错了。以 MySQL 为例,连接字符串里要带上charset=utf8mb4,并且 pymysql 驱动要在连接时确认编码。注意utf8mb4和utf8是有区别的,utf8mb4才是完整的 UTF-8 实现,能存储 emoji 和生僻字。某些老项目的数据库用的是latin1存储,那就要在 SQL 查询时用CONVERT(字段名 USING utf8mb4)做转换,这种情况比较坑,但查出来之后解决也不难。
第二种“字符错乱”通常是文件编码问题。pandas 写入 Excel 时默认使用内部 Unicode 编码,一般不会直接产生这种问题。如果你在导出 CSV 时遇到这个错,用encoding='utf-8-sig'通常能解决。utf-8-sig会写入 BOM 头,Excel 双击打开 CSV 时就能正确识别 UTF-8 编码。
4.2 内存爆掉:一次性读取的代价
批量导出的脚本在导到某个大表时突然进程被杀,这种情况十有八九是内存不足。Windows 上可能直接弹出“python.exe 已停止工作”,Linux / macOS 上则是被系统 OOM Killer 杀掉,日志里一行 killed process 都看不到。
我统计过内存分配的大致规律:pandas 读取 DataFrame 时,每列的内存占用取决于数据类型。数字列通常 8 字节一个单元格,字符串列要看长度,最保守的估算是一行数据平均 200 字节。一百万行的表,光 DataFrame 就要占用 200 MB 左右,加上 DataFrame 的复制和对象开销,实际峰值可能是估算值的两到三倍。
所以,导出前先评估数据量非常必要。如果预估会超过 50 万行,建议直接走分块导出逻辑。除了分块,还可以在查询时只选择必要的列,不要SELECT *。很多日志表有非常长的备注字段,导出来不需要,白占了大量内存。我在实际操作中会用SELECT 需要的列 FROM 表替代全字段查询,内存占用能减少 30% 到 50%。
4.3 Excel 打开慢:文件本身有两个瓶颈
导出来的文件只要超过 50 MB,业务方用 Excel 打开就会明显卡顿甚至假死。这个问题不一定是你的导出逻辑有问题,而是 Excel 对大文件的加载效率本来就有限。但有两个优化方向可以帮到你。
第一,减少 Excel 文件中的单元格数量。Excel 本身的行数上限是 1048576 行,一旦接近这个阈值,文件会非常臃肿。实际业务场景中,导出一百万行 Excel 数据对业务方来说也不方便看。很多时候,业务方真正需要的只是一个汇总报表或前若干条明细。如果你是先沟通好需求,导出前就做数据裁剪,远比事后想办法压缩文件要现实得多。
第二,选择更高效的写入引擎。实测中 xlsxwriter 在写入大 DataFrame 时比 openpyxl 快不少,而且生成的文件体积更小。用 xlsxwriter 时需要注意,它不支持对已有工作簿做二次加载,所以你需要一次性完成写入和格式设置。如果你不需要后期大量调整样式,我建议优先用 xlsxwriter:
df.to_excel("output.xlsx", index=False, engine="xlsxwriter")如果数据真的超过几十万行,且业务方明确要全量明细,那么转成 CSV 或者分多个 Excel 文件各 10 万行,是更实际的做法。CSV 文件用 Excel 打开速度非常快,只是少了格式。Excel 文件的限制不该硬扛,合理拆分才是正解。
4.4 数字变科学计数法:Excel 显示精度问题
导出包含订单号、身份证号这类长数字时,Excel 默认会显示成科学计数法,或者直接丢掉末尾几位精度。这个问题看起来小,实际上出现过业务数据对不上号的情况。原因在于 Excel 的数字精度上限是 15 位有效数字,超过之后会被舍入。
解决办法有两种。一种是在 pandas 写入时让这些列变成字符串,可以用astype(str)预转换,或者查询时在 SQL 里用CAST(字段 AS CHAR)。另一种是写入后用 openpyxl 设置单元格格式为文本:
from openpyxl import load_workbook wb = load_workbook("output.xlsx") ws = wb.active for col in range(1, ws.max_column + 1): for row in range(1, ws.max_row + 1): cell = ws.cell(row=row, column=col) if isinstance(cell.value, int) and len(str(cell.value)) >= 12: cell.number_format = "@" wb.save("output.xlsx")number_format = "@"就是把单元格显式设为文本格式。这样做之后,长数字会原样保留,不会被 Excel 的精度规则截断。不过要注意,如果源数据本来就是超长数字,最好在数据库查询阶段就转成字符串,因为 pandas 读取时大整数可能已经变成浮点数,精度已经丢失了,事后补救也补不回来。
5. 进阶优化与自动化扩展
5.1 用定时任务让导出自动化
批量导出如果只是偶尔上一次,手动执行脚本完全没问题。但如果每周每月都要跑,那就应该考虑自动化。Windows 上最轻量的方案是用“任务计划程序”,定时执行python 脚本路径。Linux / macOS 上则可以用 crontab。
以 Linux 为例,每天凌晨 3 点导出的写法是:
0 3 * * * cd /path/to/project && /path/to/env/bin/python export_all.py >> /var/log/export.log 2>&1注意这里我用的是虚拟环境里的 Python 绝对路径,不是系统的python,避免环境混乱。日志重定向到文件里也很有必要,不然脚本报错你完全不知道。定时任务跑一段时间后,建议主动去检查一下生成的文件,确认没出现新的问题。
自动化的另一个关键是幂等性。脚本每次运行时,最好先清空旧数据或生成带时间戳的文件名,避免同一天多次执行时文件互相覆盖。我常用的命名方式是加日期后缀:
from datetime import datetime today = datetime.now().strftime("%Y%m%d_%H%M%S") df.to_excel(f"user_info_{today}.xlsx", index=False)5.2 导出进度提示与中途失败恢复
导几十张表时,脚本要跑几分钟甚至更久,没有进度提示会让人很不安。最简单的做法是每导完一张表打印一行日志。如果想更直观地在控制台看进度,可以用 tqdm 库:
pip install tqdmfrom tqdm import tqdm tables = inspect(engine).get_table_names() for table in tqdm(tables): df = pd.read_sql(f"SELECT * FROM {table}", engine) df.to_excel(f"{table}.xlsx", index=False)tqdm 会自动显示进度条、预计剩余时间,运营同学看脚本执行状态一目了然。
中途失败的处理更关键。假设导出 20 张表,导到第 15 张时数据库连接断开了。没有处理机制的情况下,前面 14 张都要重新导一遍。我的解决办法是对每一张表导出后记一个标记文件,或者是把已成功的表名写入一个文本文件。下次运行前先读取这个清单,跳过已经成功的表:
import os done_tables = set() if os.path.exists("done_tables.txt"): with open("done_tables.txt") as f: done_tables = {line.strip() for line in f} for table in tables: if table in done_tables: continue try: df = pd.read_sql(f"SELECT * FROM {table}", engine) df.to_excel(f"{table}.xlsx", index=False) with open("done_tables.txt", "a") as f: f.write(table + "\n") except Exception as e: print(f"表 {table} 导出失败: {e}") continue这个“断点续跑”的设计在大批量导出时非常实用。哪怕只是几十张表的场景,也有概率遇到单表查询超时、网络闪断这类问题。加上这个机制之后,整个任务可以放心甩给定时任务,出现异常下次跑了就能自动续。
5.3 导出结果的文件归档与质量校验
自动化任务跑完不能直接结束,最好加一个简单的质量校验步骤。最基础的是对比源库中的行数和导出的行数是否一致。批量场景下,可以在导出时用len(df)记录每个表的行数,导出完成后打印一份汇总报表。
对于更严格的项目,我还会在导出后检查文件是否为空文件、文件大小是否异常。这些检查可以写成一个简单的校验脚本,在导出任务后自动执行。比如:
for table in tables: file_path = f"{table}.xlsx" if not os.path.exists(file_path) or os.path.getsize(file_path) < 100: print(f"警告: {table} 导出文件异常")虽然这种检查形式比较简单,但在实际工作中能挡住不少低级的配置错误。比如数据库连接串指到了测试环境,导出的文件行数跟预估差很多,一查就发现了。
结语:个人经验与建议
这套 Python 批量导出数据库数据至 Excel 文件的方案,我在本地测试环境和模拟项目的实际任务里反复用过很多次。最深的体会是:导出脚本的核心不在于代码有多漂亮,而在于面对各种各样的脏数据、刁钻格式和反复需求时能不能稳住。连接字符串统一用 SQLAlchemy、数据转换靠 pandas、Excel 落盘用 openpyxl 或 xlsxwriter,这套组合几乎覆盖了我见过的所有导出需求。
最后分享一个小技巧:导出的 Excel 文件里,日期时间字段经常出现2024-01-01 00:00:00这样的字符串,但业务方往往只想要2024-01-01。处理办法是在 pandas 里先做转换,df['日期'] = pd.to_datetime(df['日期']).dt.strftime('%Y-%m-%d'),这样导出的单元格就是纯文本格式的短日期,不会被 Excel 自动识别成时间戳再变出时长的小时部分。类似的数据清洗需求,尽量在写入 Excel 之前处理完,比导出来之后再修改要高效得多。
如果你看完这篇文章准备自己动手,我建议先拿一张小表跑通整条流程,再加格式、再补错误处理、再上调度。一点点加复杂度,每加一层都能明确知道它解决了什么问题。这样踩坑最少,方案也最贴合你自己的业务场景。