1. 为什么需要批量处理Excel和CSV文件?
在日常办公和数据处理工作中,我们经常会遇到需要处理大量Excel和CSV文件的情况。比如财务人员需要汇总多个部门的报表,数据分析师需要清洗来自不同渠道的数据,或者开发人员需要将Excel数据转换为CSV格式以便程序处理。
手动一个个打开文件进行操作不仅效率低下,而且容易出错。我曾经接手过一个项目,需要将300多个Excel文件中的数据合并到一个总表中。如果手动操作,不仅需要花费大量时间,还可能在复制粘贴过程中出错。而使用Python脚本,整个过程只需要几分钟就能完成,而且可以确保数据的准确性。
Python作为一门强大的编程语言,提供了多个库可以方便地处理Excel和CSV文件。其中最常用的是:
- openpyxl:专门用于处理Excel 2010及以后版本的xlsx文件
- pandas:强大的数据分析库,可以轻松处理Excel和CSV文件
- csv:Python标准库中的CSV处理模块
2. 环境准备与基础配置
2.1 安装必要的Python库
在开始之前,我们需要确保已经安装了Python环境(建议使用Python 3.6及以上版本)和必要的库。可以通过以下命令安装:
bash复制pip install openpyxl pandas xlrd
这里解释一下各个库的作用:
- pandas:提供了DataFrame数据结构,可以方便地处理表格数据
- openpyxl:用于读写Excel 2010 xlsx/xlsm/xltx/xltm文件
- xlrd:用于读取旧版Excel文件(.xls)
注意:如果你使用的是Mac系统,可能会遇到权限问题,可以在命令前加上
--user参数,如pip install --user pandas
2.2 准备测试数据
为了更好地演示,我们可以创建一些测试用的Excel和CSV文件。这里提供一个简单的Python脚本来生成测试数据:
python复制import os
import pandas as pd
import numpy as np
# 创建测试目录
os.makedirs('test_data', exist_ok=True)
# 生成5个Excel测试文件
for i in range(1, 6):
data = {
'姓名': [f'张三{i}', f'李四{i}', f'王五{i}'],
'年龄': np.random.randint(20, 50, 3),
'部门': ['销售', '技术', '人事']
}
df = pd.DataFrame(data)
df.to_excel(f'test_data/员工数据_{i}.xlsx', index=False)
# 生成5个CSV测试文件
for i in range(1, 6):
data = {
'产品ID': [f'P00{i}', f'P00{i+1}', f'P00{i+2}'],
'销量': np.random.randint(100, 1000, 3),
'单价': np.random.uniform(10.5, 99.9, 3).round(2)
}
df = pd.DataFrame(data)
df.to_csv(f'test_data/销售数据_{i}.csv', index=False, encoding='utf-8-sig')
运行这个脚本后,你会在当前目录下看到一个test_data文件夹,里面包含5个Excel文件和5个CSV文件。
3. 批量读取Excel文件
3.1 使用pandas读取单个Excel文件
在批量处理之前,我们先看看如何读取单个Excel文件:
python复制import pandas as pd
# 读取Excel文件
file_path = 'test_data/员工数据_1.xlsx'
df = pd.read_excel(file_path)
# 查看前几行数据
print(df.head())
pandas的read_excel()函数提供了很多有用的参数:
- sheet_name:指定要读取的工作表,可以是名称或索引
- header:指定哪一行作为列名
- usecols:指定要读取的列
- dtype:指定列的数据类型
3.2 批量读取多个Excel文件
现在我们来批量读取test_data目录下的所有Excel文件:
python复制import os
import pandas as pd
# 获取目录下所有Excel文件
folder_path = 'test_data'
excel_files = [f for f in os.listdir(folder_path) if f.endswith('.xlsx')]
# 创建一个空的DataFrame用于存储所有数据
all_data = pd.DataFrame()
# 遍历所有Excel文件并读取
for file in excel_files:
file_path = os.path.join(folder_path, file)
df = pd.read_excel(file_path)
all_data = pd.concat([all_data, df], ignore_index=True)
# 查看合并后的数据
print(all_data)
在实际项目中,你可能会遇到以下问题:
- 不同Excel文件可能有不同的列名或列顺序
- 某些文件可能有额外的标题行或注释行
- 文件可能使用不同的编码
针对这些问题,我们可以改进代码:
python复制# 改进版的批量读取代码
all_data = pd.DataFrame()
columns_order = ['姓名', '年龄', '部门'] # 定义统一的列顺序
for file in excel_files:
file_path = os.path.join(folder_path, file)
try:
df = pd.read_excel(file_path)
# 统一列名(如果不同文件使用不同的列名)
if 'name' in df.columns:
df = df.rename(columns={'name': '姓名'})
# 确保所有文件有相同的列
for col in columns_order:
if col not in df.columns:
df[col] = None # 添加缺失的列
# 按统一顺序排列列
df = df[columns_order]
all_data = pd.concat([all_data, df], ignore_index=True)
except Exception as e:
print(f"处理文件{file}时出错: {str(e)}")
# 保存合并后的数据
all_data.to_excel('合并后的员工数据.xlsx', index=False)
4. 批量处理CSV文件
4.1 读取和写入CSV文件的基本操作
CSV(Comma-Separated Values)是一种常见的纯文本格式,比Excel文件更轻量级。Python中有多种处理CSV文件的方法:
python复制# 方法1:使用pandas
import pandas as pd
# 读取CSV
df = pd.read_csv('test_data/销售数据_1.csv')
# 写入CSV
df.to_csv('处理后的销售数据.csv', index=False)
# 方法2:使用Python标准库csv
import csv
# 读取CSV
with open('test_data/销售数据_1.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# 写入CSV
with open('处理后的销售数据.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['产品ID', '销量', '单价']) # 写入标题
writer.writerow(['P001', 100, 25.5]) # 写入数据行
注意:在Windows系统上使用csv模块时,建议在open()函数中添加newline=''参数,避免出现空行问题。
4.2 批量处理CSV文件的实用技巧
在实际项目中,我们经常需要对多个CSV文件执行相同的操作。下面是一个批量处理CSV文件的示例:
python复制import os
import pandas as pd
# 获取所有CSV文件
csv_files = [f for f in os.listdir('test_data') if f.endswith('.csv')]
# 批量处理:计算每个产品的销售额并保存
for file in csv_files:
file_path = os.path.join('test_data', file)
df = pd.read_csv(file_path)
# 计算销售额
df['销售额'] = df['销量'] * df['单价']
# 保存处理后的文件
output_file = file.replace('.csv', '_处理结果.csv')
df.to_csv(os.path.join('processed_data', output_file), index=False)
处理CSV文件时常见的编码问题:
- UTF-8:最通用的编码方式
- GBK/GB2312:中文Windows系统常用的编码
- UTF-8 with BOM:某些系统会在文件开头添加BOM标记
为了避免编码问题,可以这样处理:
python复制# 尝试不同编码读取CSV文件
encodings = ['utf-8', 'gbk', 'utf-8-sig', 'latin1']
for enc in encodings:
try:
df = pd.read_csv('problematic.csv', encoding=enc)
print(f"成功使用{enc}编码读取文件")
break
except UnicodeDecodeError:
continue
5. Excel与CSV格式转换
5.1 将Excel批量转换为CSV
在某些场景下,我们需要将Excel文件转换为CSV格式,比如:
- 需要与其他系统交换数据
- CSV文件更小,便于传输
- 某些程序只能处理CSV格式
下面是将Excel批量转换为CSV的代码:
python复制import os
import pandas as pd
# 创建输出目录
os.makedirs('csv_output', exist_ok=True)
# 获取所有Excel文件
excel_files = [f for f in os.listdir('test_data') if f.endswith('.xlsx')]
for file in excel_files:
file_path = os.path.join('test_data', file)
# 读取Excel文件
df = pd.read_excel(file_path)
# 生成CSV文件名
csv_file = file.replace('.xlsx', '.csv')
csv_path = os.path.join('csv_output', csv_file)
# 保存为CSV
df.to_csv(csv_path, index=False, encoding='utf-8-sig')
print(f"已成功将{len(excel_files)}个Excel文件转换为CSV格式")
5.2 将CSV批量转换为Excel
有时我们也需要将CSV转换为Excel,比如:
- 需要添加复杂的格式或公式
- 需要创建多个工作表
- 需要保护某些单元格
下面是将CSV批量转换为Excel的代码:
python复制import os
import pandas as pd
# 创建输出目录
os.makedirs('excel_output', exist_ok=True)
# 获取所有CSV文件
csv_files = [f for f in os.listdir('test_data') if f.endswith('.csv')]
for file in csv_files:
file_path = os.path.join('test_data', file)
# 读取CSV文件
df = pd.read_csv(file_path)
# 生成Excel文件名
excel_file = file.replace('.csv', '.xlsx')
excel_path = os.path.join('excel_output', excel_file)
# 保存为Excel
df.to_excel(excel_path, index=False)
print(f"已成功将{len(csv_files)}个CSV文件转换为Excel格式")
6. 高级批量处理技巧
6.1 处理大型Excel文件
当处理大型Excel文件时,可能会遇到内存不足的问题。这时可以使用以下方法:
- 分块读取:
python复制# 分块读取大型Excel文件
chunk_size = 1000 # 每次读取1000行
chunks = pd.read_excel('large_file.xlsx', chunksize=chunk_size)
for chunk in chunks:
# 处理每个数据块
process_chunk(chunk)
- 使用openpyxl的只读模式:
python复制from openpyxl import load_workbook
# 使用只读模式打开大型Excel文件
wb = load_workbook('large_file.xlsx', read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
# 逐行处理数据
process_row(row)
6.2 多线程批量处理
当需要处理大量文件时,可以使用多线程提高效率:
python复制import os
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
def process_file(file_path):
try:
if file_path.endswith('.xlsx'):
df = pd.read_excel(file_path)
elif file_path.endswith('.csv'):
df = pd.read_csv(file_path)
# 在这里添加你的处理逻辑
processed_df = some_processing_function(df)
# 保存处理结果
output_path = os.path.join('processed', os.path.basename(file_path))
if file_path.endswith('.xlsx'):
processed_df.to_excel(output_path, index=False)
else:
processed_df.to_csv(output_path, index=False)
return True
except Exception as e:
print(f"处理文件{file_path}时出错: {str(e)}")
return False
# 获取所有需要处理的文件
all_files = []
for root, dirs, files in os.walk('data_folder'):
for file in files:
if file.endswith(('.xlsx', '.csv')):
all_files.append(os.path.join(root, file))
# 使用线程池并行处理
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(process_file, all_files))
print(f"成功处理了{sum(results)}/{len(results)}个文件")
6.3 自动化处理复杂Excel文件
对于包含多个工作表、特殊格式或公式的复杂Excel文件,可以使用openpyxl进行更精细的控制:
python复制from openpyxl import load_workbook
# 加载Excel文件
wb = load_workbook('complex_file.xlsx')
# 获取所有工作表名
print(wb.sheetnames)
# 处理每个工作表
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
# 读取单元格数据
for row in ws.iter_rows(min_row=2, values_only=True): # 跳过标题行
process_row(row)
# 修改单元格值
ws['A1'] = '新的标题'
# 添加公式
ws['D2'] = '=B2*C2'
# 设置单元格格式
from openpyxl.styles import Font, Alignment
ws['A1'].font = Font(bold=True)
ws['A1'].alignment = Alignment(horizontal='center')
# 保存修改后的文件
wb.save('modified_complex_file.xlsx')
7. 实际项目中的经验分享
在多年的数据处理工作中,我总结了以下经验教训:
-
文件编码问题:始终明确指定文件编码。对于中文内容,建议使用'utf-8-sig'编码,它可以处理BOM标记并兼容大多数系统。
-
路径处理:使用os.path.join()来拼接文件路径,而不是直接用字符串拼接。这样可以确保代码在不同操作系统上都能正常工作。
-
异常处理:批量处理文件时,一定要添加适当的异常处理。一个损坏的文件不应该导致整个程序崩溃。
-
内存管理:处理大型文件时,注意内存使用情况。考虑使用分块读取或只读模式。
-
日志记录:在批量处理过程中,记录处理了哪些文件、成功和失败的情况。这有助于后续排查问题。
-
性能优化:对于大批量文件处理,可以考虑:
- 使用多线程或多进程
- 避免在循环中重复创建相同的对象
- 使用更高效的数据结构
-
数据验证:在处理后验证数据的完整性,比如检查行数、关键字段是否完整等。
下面是一个综合了这些经验的完整示例:
python复制import os
import pandas as pd
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='batch_processing.log'
)
def validate_data(df):
"""验证数据完整性"""
if df.empty:
raise ValueError("数据为空")
if '姓名' not in df.columns:
raise ValueError("缺少'姓名'列")
return True
def process_single_file(file_path, output_dir):
"""处理单个文件"""
try:
# 根据文件类型选择读取方式
if file_path.endswith('.xlsx'):
df = pd.read_excel(file_path)
elif file_path.endswith('.csv'):
df = pd.read_csv(file_path, encoding='utf-8-sig')
else:
logging.warning(f"跳过不支持的文件类型: {file_path}")
return False
# 数据验证
validate_data(df)
# 数据处理(示例:添加处理日期列)
df['处理日期'] = datetime.now().strftime('%Y-%m-%d')
# 生成输出文件名
base_name = os.path.basename(file_path)
output_name = f"processed_{base_name}"
output_path = os.path.join(output_dir, output_name)
# 保存处理结果
if file_path.endswith('.xlsx'):
df.to_excel(output_path, index=False)
else:
df.to_csv(output_path, index=False, encoding='utf-8-sig')
logging.info(f"成功处理文件: {file_path}")
return True
except Exception as e:
logging.error(f"处理文件{file_path}时出错: {str(e)}", exc_info=True)
return False
def batch_process_files(input_dir, output_dir):
"""批量处理文件"""
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
# 获取所有支持的文件
supported_ext = ('.xlsx', '.csv')
files_to_process = [
os.path.join(input_dir, f) for f in os.listdir(input_dir)
if f.endswith(supported_ext)
]
if not files_to_process:
logging.warning(f"在目录{input_dir}中未找到可处理的文件")
return
logging.info(f"开始批量处理{len(files_to_process)}个文件")
# 处理每个文件
success_count = 0
for file_path in files_to_process:
if process_single_file(file_path, output_dir):
success_count += 1
logging.info(f"处理完成。成功: {success_count}, 失败: {len(files_to_process)-success_count}")
# 使用示例
batch_process_files('test_data', 'processed_data')
这个示例展示了如何处理一个完整的批量处理任务,包括日志记录、异常处理、数据验证等功能。你可以根据自己的需求修改数据处理逻辑。
