干过几年数据处理的人,谁还没被Excel卡过几次。月初要对账的时候,邮箱里躺着几十个各分公司发来的表格,格式还不统一,有的叫“报表.xlsx”,有的是“数据(1).csv”,打开一看列名对不上,领导还催着下班前出汇总。这时候你大概会想:要是有个工具能自动把这些文件全读进来,统一整理好再吐出去就好了。这个想法,就是今天这篇文章要聊的核心——用Python批量处理Excel和CSV文件。
Python在这件事上的优势不是“能处理”,而是“批量”和“可复用”。你写一次处理逻辑,就能对一个文件夹里几百个文件重复执行;下次数据再来一批,换条路径跑一遍就行。这个能力对经常跟表格打交道的人很实用——无论是财务月度汇总、运营数据分析、GIS属性表整理,还是把临时拉取的CSV转成规范的Excel报送格式,都能用同一套思路快速落地。
这篇文章我会从环境准备讲起,覆盖读取CSV、读写Excel、批量循环处理整个文件夹,再到合并多个表格、按条件拆分、跨格式转换这些高频需求,最后整理编码、日期、性能这些容易踩坑的点。适合刚接触Python的办公人员,也适合想把手头重复工作自动化的数据分析初学者。文章里的代码都能直接复制运行,你只需要改一下文件路径。
1. 整体设计:先把“批量处理”几个字拆清楚
1.1 手工操作的真正痛点
很多人觉得Excel批量处理无非就是“复制粘贴”,但真正上手就知道没那么简单。先说格式不统一:有的报表列名是“订单号”,有的是“订单编号”,还有的是“Order ID”;有的日期列存的是“2024-01-15”这种标准格式,有的却是“20240115”纯数字,更糟的还有“1/15”这种Excel自动转出来的谜之格式。几十个文件堆在一起,光对齐表头就能消耗掉大半天。
再说合并场景。把几十个文件的数据拼到同一个总表里,手工操作除了复制粘贴,还得注意别漏行、别重复。粘贴时如果目标表已经有关联公式,偶尔还会出现单元格错位的情况。文件一多Excel本身也会卡,尤其是单个文件几万行数据时,滚动、筛选都迟钝,复制粘贴时稍不注意就失去响应。
CSV文件就更尴尬了。用Excel直接打开CSV,中文经常乱码,因为很多CSV是UTF-8编码,而老版本Excel默认用ANSI解析;如果CSV里某条数据里恰好包含逗号或者换行,Excel打开时还会裂成多列,看着就头疼。这些情况用Python处理就很简单,编码问题可以显式指定,带引号的字段也能正确解析。
1.2 为什么Python方案能解决问题
Python解决这类问题的核心就一条:把所有文件当成“同一种结构的数据”,用同一套逻辑循环处理。你在第一个文件上验证通过的操作,对第100个文件同样生效,不会因为文件多了就出错。
案例是最直观的。假设手头有20个门店的销售CSV,每个文件几千行,需要合并成一个总表。手工做至少要半小时,还可能出错;用Python处理,从读取到写出合并结果也就几秒。再比如每个月要把系统导出的CSV转成固定格式的Excel报送表,手工做一次要配置导入、调整列宽、改数字格式,用Python把这些操作写进脚本后,每个月双击一下就完成了。
更重要的一点是可复用性。这次写好的脚本,下个月数据来了换个路径继续用;这个季度想额外加一个汇总Sheet,改几行代码就能实现。这比每次手工处理要可靠得多。
1.3 技术选型:pandas是主力,openpyxl是补充
做Excel和CSV批量处理,Python里主要用三个库:pandas、openpyxl、csv。csv是Python自带的轻量模块,适合读简单的CSV文件,但数据处理能力弱,本文不做重点。核心主力是pandas和openpyxl。
pandas是这个场景下的标准答案。它把表格数据抽象成DataFrame——你可以理解成一张放在内存里的Excel表,有行有列,能按条件筛选、按列拼接、按关键字分组。它读取CSV和Excel都非常方便,几百行代码的事通常几行就完成。处理大数据量时还支持分块读取,不会一下子把内存吃满。
openpyxl则专门负责Excel的“精细操作”。pandas能读写Excel的数值和文本,但如果你想修改单元格底色、设置边框、调整冻结窗格、写入图片,就需要用openpyxl。实际项目中两者通常配合使用:pandas负责数据整理,openpyxl负责最后的格式美化。
| 库名 | 适用场景 | 优势 | 局限 |
|---|---|---|---|
| pandas | 数据分析、合并、筛选、分组、批量转换 | 代码短、性能好、生态成熟 | 对单元格样式控制较弱 |
| openpyxl | 读写Excel格式细节 | 可操作样式、公式、图表 | 大数据量下性能一般 |
| csv | 简单CSV读写 | 无需安装、轻量 | 需要手工处理类型转换 |
2. 环境准备:装好Python和两个核心库就能跑
2.1 安装Python:注意那个“Add to PATH”勾选
Python安装本身不难,但很多人卡在环境变量上。去Python官网下载对应系统的安装包,Windows用户安装时有一个关键的勾选——“Add Python to PATH”,这个必须勾上。它把Python的可执行文件路径加进系统环境变量,这样你在命令行直接敲python就能启动,不用每次去找安装目录。忘了勾也别慌,可以手动把Python安装路径添加到系统环境变量里,或者卸载重装一次。
装好之后,打开命令行(Windows是CMD或PowerShell,macOS是终端),输入:
python --version能看到类似Python 3.12.x的输出就说明安装成功。Mac和Linux用户有些系统自带Python,但版本可能偏老,建议直接用官网安装包装最新的稳定版,省得后面遇到包不兼容的问题。
提示:Python 3.8以上版本都能流畅运行本文的代码。如果电脑里同时装了多个Python版本,命令会区分成
python和python3,使用时保持一致就行。
2.2 安装pandas和openpyxl:一条命令搞定
Python装好后,安装第三方库统一用pip命令。打开命令行,执行:
pip install pandas openpyxl看到“Successfully installed”就说明装好了。如果网络慢或者安装超时,可以切换到国内镜像源,清华和阿里云的源速度通常不错:
pip install pandas openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple-i参数是指定下载源地址,这一步在很多教程里被省略,实际上对国内用户来说非常实用。
2.3 虚拟环境:管理依赖的整洁方案
遇到多个项目依赖不同版本的库时,虚拟环境能避免“库版本打架”。简单说,虚拟环境就是一个独立的Python运行目录,你在里面装的库不影响全局环境,全局环境里的库也不影响它。创建和使用的命令只有三条:
python -m venv myenv创建名为myenv的虚拟环境,然后激活:
# Windows myenv\Scripts\activate # macOS / Linux source myenv/bin/activate激活后命令行前面会顶着(myenv),这时候再pip install就只会装进这个环境里。对刚入门的人来说,不建虚拟环境其实也够用,但如果你同时做着数据分析、爬虫、Web开发等多个方向,建议从一开始就养成用虚拟环境的习惯。
3. 核心实操:从读单个文件到循环处理整个文件夹
3.1 读取CSV:第一个必学的函数
处理的起点永远是“把文件里的数据读进来”。pandas里读取CSV的函数是pd.read_csv(),最基本用法是把文件路径传进去:
import pandas as pd df = pd.read_csv('销售数据.csv') print(df.head()) # 查看前5行df.head()能快速确认数据是否成功读取,这是我在每个脚本里都会写的第一步调试语句。中文乱码问题在读取CSV时非常常见,原因也很简单:文件本身用UTF-8编码,但Excel默认用GBK或ANSI打开。pandas默认按UTF-8读,遇到GBK编码的文件会直接报错或者出现乱码。解决办法是显式指定encoding参数:
df = pd.read_csv('销售数据.csv', encoding='gbk')记不住每种文件的编码也不丢人,报错时就换一个参数试,UTF-8和GBK两个编码几乎覆盖了国内办公场景里的所有CSV文件。如果两个都试过还是乱码,可以用chardet库检测文件实际编码,但这个场景比较少见。
注意:有些CSV文件的分隔符不是逗号,而是分号、制表符或竖线。
pd.read_csv()里可以用sep参数指定,比如sep=';'。打开文件看到数据挤在一列里时,首先检查的就是分隔符和编码这两个参数。
3.2 读写Excel:几个参数决定成败
Excle读取用pd.read_excel():
df = pd.read_excel('订单明细.xlsx', sheet_name='Sheet1')sheet_name可以指定工作表名,也可以用索引,比如sheet_name=0代表第一个工作表;传一个列表还能一次读多个工作表。写入Excel用df.to_excel():
df.to_excel('输出文件.xlsx', index=False)这里index=False是必须养成的习惯。df的行号默认以整数序列保存,如果不加这个参数,输出文件里会多出一列无意义的“序号”,后面处理数据时会干扰表头对齐。我最初就是忘掉这个参数,输出后发现汇总表的第1列全是1、2、3这样的数字,还得回头再删。
如果你想把多个数据表分别写入同一个Excel文件的不同工作表,需要用到pd.ExcelWriter:
with pd.ExcelWriter('月度报表.xlsx') as writer: df_sales.to_excel(writer, sheet_name='销售') df_stock.to_excel(writer, sheet_name='库存')with语句会在代码块结束后自动保存并关闭文件,不用手动调用保存方法,省心且不容易出错。
3.3 批量循环:让脚本自己处理整个文件夹
批量处理的核心就是“找到所有文件,逐个执行同样的操作”。Python里获取文件列表最方便的两个工具是glob和os.listdir。以glob为例,假设一个文件夹里放了30个CSV文件:
import glob import pandas as pd file_list = glob.glob('data/*.csv') print(file_list) # 看看匹配到了哪些文件glob返回一个列表,里面的每个元素都是匹配到的文件路径。拿到列表之后,用一个for循环逐个处理就行。一个最简单的批量转格式脚本长这样:
import glob import pandas as pd file_list = glob.glob('data/*.csv') # 匹配所有CSV文件 for file in file_list: df = pd.read_csv(file, encoding='utf-8') df['source_file'] = file # 加一列,标记数据来自哪个文件 df.to_excel(file.replace('.csv', '.xlsx'), index=False) print(f'{file} 处理完成,共 {len(df)} 行')这个脚本做的事情有:读取CSV、给每一行打上来源标记、转成Excel文件、打印处理日志。运行一遍,一个文件夹里所有CSV文件都转成了Excel,文件名不变,只替换了扩展名。这个模式可以迁移到各种操作上——筛选特定条件、新增计算列、删除重复值,都可以塞进这个循环体里。
处理日志里的f'{file} 处理完成,共 {len(df)} 行'是f-string语法,用来在字符串里嵌入变量值。它除了让你在执行时能实时看到进度,跑完后还能核对每个文件是否被正确读取,避免“静默失败”。
4. 进阶玩法:合并、拆分、格式转换一次讲透
4.1 合并多个表格:pd.concat一行解决
把几十个结构相同或相似的表格合并成一张总表,是办公场景里最高频的需求。pandas的做法是先逐个读取,再统一合并。
import glob import pandas as pd file_list = glob.glob('data/*.xlsx') df_list = [] for file in file_list: df = pd.read_excel(file) df_list.append(df) df_all = pd.concat(df_list, ignore_index=True) df_all.to_excel('合并总表.xlsx', index=False)pd.concat沿着行的方向把多个DataFrame堆叠起来,ignore_index=True表示重新生成连续的行号,否则合并后的表行号会保留各文件的原始索引,看起来是乱的。
这里有一个常见问题:如果各文件的列顺序不一致,合并后会不会错位?答案是不会。pd.concat默认按列名对齐,而不是按位置对齐。也就是说A文件列名叫“金额”,B文件列名叫“金额”,会正确对齐到同一列;如果某列只有一个文件有,另一个文件没有,缺失部分会自动填成NaN(空值)。这比手工粘贴安全得多。
4.2 按条件拆分:groupby + to_excel组合
批量操作不只是“合并”,把一个大表拆成多个小表也是常见的需求。比如一张全国门店的销售总表,希望按城市拆分成单独的文件,每个城市一个Excel。用groupby就能实现:
import pandas as pd df = pd.read_excel('全国销售总表.xlsx') for city, group in df.groupby('城市'): group.to_excel(f'拆分结果/{city}.xlsx', index=False) print(f'{city} 已导出,共 {len(group)} 行')先按“城市”列分组,得到每个组对应的数据子集,再分别导出。第一次跑之前记得先建好拆分结果这个文件夹,不然会报文件路径不存在的错误。运行结束后,同样可以用文件夹里生成的文件数量与分组数核对,确保每次导出都成功。
除了导出多个文件,也可以把一个Excel文件的多个Sheet拆出来做进一步处理。pd.read_excel()的sheet_name=None可以一次读取所有工作表:
all_sheets = pd.read_excel('多表.xlsx', sheet_name=None) for sheet_name, df in all_sheets.items(): print(sheet_name, len(df))这时all_sheets是一个字典,键是工作表名,值是对应的DataFrame,拿到之后想怎么处理都方便。
4.3 Excel和CSV互转:注意格式损失
Excel转CSV是导出数据给其他系统用的高频操作,反过来把CSV变成Excel做后续展示也很常见。核心代码在上面已经出现过,但要提醒一条细节:CSV只保存数据和逗号分隔符,不保存Excel里的单元格格式、列宽、颜色、公式。如果你原表里有用公式计算的列,转成CSV后再读回来,数值变成了静态结果,公式本身丢了。这一点在转换前要想清楚。
反过来,CSV转Excel也会遇到问题。CSV里经常有超长数字串,比如订单编号、身份证号,用Excel直接打开会自动转成科学计数法,看着是1.24015E+17这种,完全没法用。用pandas读取时,可以先把这一列转成字符串类型再写入Excel:
df = pd.read_csv('订单.csv', dtype={'订单编号': str}) df.to_excel('订单.xlsx', index=False)dtype参数指定读入时的数据类型,把订单编号按字符串读取,就等于告诉pandas“别把它当数字处理”。或者读取后再转换也行:
df['订单编号'] = df['订单编号'].astype(str)这一步看着简单,但能避免很多后续对账时的数据错乱问题。
4.4 跨场景实用案例:GIS、量化、文档表格和AI流程
批量处理表格的应用场景远不止财务汇总和报表整理,实际工作里还有几个高频场景值得单独说。
GIS方向,很多人用ArcGIS做制图,经常需要把Excel里的点坐标数据导入GIS生成要素文件。Excel能转CSV,但ArcGIS读CSV时对表头、字段类型很敏感,通常建议把表头改成英文字段名,经纬度单独成列,数值字段不要带千分位分隔符。这一步交给Python做是轻车熟路:读取原始Excel、重命名列、清洗数值、导出成规范的CSV文件,再进ArcGIS的“添加XY数据”工具,比手工在Excel里改一个小时靠谱得多。
量化交易方向,行情数据常常是CSV格式,一天一个文件或者一个月一个文件,做回测前需要把历史数据合并、清洗、统一时间戳。pandas的pd.to_datetime()可以统一日期格式,df.sort_values()按时间排序,df.drop_duplicates()去重,都是量化数据预处理里每天要用的操作。
文档方向,Markdown里偶尔需要转表格到Excel,或者从Excel反向生成Markdown表格。pandas读出Excel数据后,用df.to_markdown()可以一步生成Markdown格式的表格,复制到文档就能用;反过来,把Markdown表格先转成CSV再用pandas读进来也方便。
AI知识库和本地文档处理方向也有用武之地。像RAGFlow这类知识库工具支持批量导入文档,但文档里的表格往往需要先转成规范格式才能被正确解析。这时候用Python把Excel、CSV统一清洗成UTF-8编码的标准表格文件,再导入知识库,能有效提高后续检索和问答的准确率。我自己在实际操作中,就对一批杂乱的CSV做过去除空行、统一列名、转成标准分隔符的预处理,后续导入流程顺畅了很多。
5. 避坑手册:编码、日期、大数据量是重灾区
5.1 编码问题:UTF-8和GBK的纠葛
CSV读取中最常见的报错就是UnicodeDecodeError,打开控制台看到这个红字,十有八九是编码参数不对。解决方案很简单,先试encoding='utf-8',报错就换encoding='gbk'。如果你自己写CSV文件,建议全程用utf-8:
df.to_csv('输出.csv', index=False, encoding='utf-8-sig')注意这里我用了utf-8-sig而不是utf-8。utf-8-sig会在文件开头写入一个字节序标记(BOM),Excel识别这个标记后能正确按UTF-8解析,不会乱码。如果用纯utf-8,Excel打开还是乱码,这是很多人在本地CSV转Excel后遇到的“薛定谔的乱码”问题。一个小参数的差别,决定文件能不能被Excel正常打开,这事我踩过好几次了。
5.2 日期数据:Excel序列号与字符串的纠缠
日期列是另一个让人头大的部分。Excel内部把日期存成从1900年1月1日起的序列号,比如45292代表2024年1月15日,但CSV里导出的日期往往是“2024-01-15”这样的字符串。pandas读取时,日期列默认会是字符串类型(object),如果直接拿去排序或过滤,得到的结果完全不对,因为字符串比较的“2024-11”会排在“2024-9”前面。
解决办法是读取后统一转成真正的日期类型:
df['下单日期'] = pd.to_datetime(df['下单日期'])pd.to_datetime()能自动识别多种常见日期格式并统一成标准格式。转换之后做筛选、排序、按月汇总,语义就对了。如果源文件是Excel,可以在读取时用parse_dates参数指定需要转换的列,一步到位:
df = pd.read_excel('订单.xlsx', parse_dates=['下单日期'])处理完日期如果要按月份统计,可以把日期列转成月份标记:
df['月份'] = df['下单日期'].dt.to_period('M')然后再用groupby('月份')汇总,比手工在Excel里拖数据透视表灵活得多。
5.3 性能问题:循环不是万能的
批量处理的文件一多、数据量一大,运行时长的差距就很明显。几十个几万行的文件用pandas处理基本毫无压力,但如果你一个文件就上百万行,而且循环体里逐行做操作,那脚本跑起来就会越来越慢。
逐行操作是新人最常犯的性能错误:
# 这个写法不推荐,太慢了 for i in range(len(df)): df.loc[i, '新列'] = df.loc[i, '旧列'] * 2pandas里“逐行修改”的效率远低于“整列计算”:
# 这个写法好得多 df['新列'] = df['旧列'] * 2整列计算在底层由C语言实现,性能高两个数量级。类似地,筛选条件数据用布尔索引,不要用循环遍历判断。
5.4 大文件分块:chunksize处理内存溢出的招
单文件特别大时(几GB的CSV很常见),pd.read_csv()一次性读入可能直接把内存吃满,轻则卡死,重则进程被杀。这时候用chunksize参数分批读取:
chunk_iter = pd.read_csv('超大.csv', chunksize=100000) for chunk in chunk_iter: # 对每个chunk处理 print(len(chunk))chunksize=100000表示每次只读取10万行,处理完再读下一批。如果你的任务是统计这类大文件的总行数、总和、平均值,可以边读边累加。这个思路和“一次不要吃太多,分几口吃”是一样的,适合超大文件的统计和过滤场景。
5.5 写出路径与文件覆盖
脚本跑完找不到输出文件的经历,很多人都遇到过。原因往往是输出路径没有写完整,或者文件保存到了脚本运行时的当前目录,而这个目录和你打开文件夹的位置不一样。建议在脚本开头就把输入输出路径定义清楚:
input_dir = 'D:/工作/原始数据/' output_dir = 'D:/工作/处理结果/'运行脚本之前,确认输出文件夹已经创建好。也可以在脚本里自动创建文件夹:
import os os.makedirs(output_dir, exist_ok=True)exist_ok=True表示文件夹已存在时不报错。这个参数是“幂等”的思路——同一个脚本跑第二次不会因为路径已存在而中断,批量处理的脚本更应该做到这一点。
5.6 和Excel插件的联动问题
最后补充一个和Excel自身有关的坑。有些电脑上Excel的“加载项”可能提示被禁用,比如Power Query、分析工具库等,这类情况多数是因为组件被关闭或版本限制,通常不影响Python直接读写文件。但如果你的目标是用Python生成一个带宏或带复杂格式的Excel文件,需要注意openpyxl保持的格式有限。遇到和Excel本身的交互问题,比如Ctrl+V粘贴失效,这更多是Excel软件设置层面的问题,和Python脚本无关,排查时不要混在一起。
6. 最后的实战总结:从写脚本到负责任地交付
前面讲了这么多,最后落回到实际工作的一个完整案例上。假设你是运营,每周要出一份销售周报,数据源是系统导出的10个CSV,你需要把CSV按门店合并,去掉重复订单,只保留交易金额大于0的记录,加一列“周内日期序号”方便透视,最后输出一个格式统一的Excel。完整脚本大概是这样的:
import glob import pandas as pd # 1. 读取所有CSV文件 file_list = glob.glob('data/*.csv') df_list = [] for file in file_list: df = pd.read_csv(file, encoding='utf-8-sig') df_list.append(df) # 2. 合并成一张表 df = pd.concat(df_list, ignore_index=True) # 3. 去重、过滤 df = df.drop_duplicates(subset='订单号') df = df[df['交易金额'] > 0] # 4. 添加周内日期序号 df['下单日期'] = pd.to_datetime(df['下单日期']) df['周内序号'] = df['下单日期'].dt.dayofweek + 1 # 5. 输出Excel df.to_excel('销售周报.xlsx', index=False) print(f'处理完成,共导出 {len(df)} 条有效记录')这段代码把一个需要人工操作半小时以上的流程压缩成了电脑自动执行的几秒钟。整个过程里的关键点:读取时指定编码,合并时忽略索引,去重时明确依据列,过滤时保留有效数据,最后输出时关掉索引列。
我在实际使用中最深的体会是:脚本本身不难,难的是处理那些“脏数据”。文件命名不规律、列名大小写不一致、日期格式变来变去、数字列里混着“—”“待定”之类的文本,这些才是批量处理里真正消耗精力的地方。所以每次跑完脚本,一定要抽查几行结果,确认数据没有系统性偏差,再决定是否用于汇报或入库。
如果你刚开始学,建议先从一个小场景练手:找十几张同类的CSV,写一个只做“读取、合并、输出”的三行代码脚本,跑通后再逐渐加入筛选、去重、清洗逻辑。这个过程中不断积累处理异常的经验,以后遇到再乱的表格,打开Python心里都不慌。