Python自动化实操:用xlrd和xlwt读写Excel .xls文件
这次我们来处理“100天精通Python”系列的实操日第41天自动化读写Excel。很多办公自动化需求最终都会落到几个熟悉的包上Pandas能处理分析openpyxl能处理新版xlsx而xlrd和xlwt一直是老牌.xls文件读写方案。这篇文章会集中讲清楚两个模块最常用的参数、代码写法、批量任务姿势和排错思路内容结构是“先看能力边界再装环境然后边写代码边验证”不需要GPU不需要装IDE插件一个普通办公笔记本就可以跑完所有示例。为什么今天专门把xlrd和xlwt拎出来讲因为在老系统、财务报表、企业内部系统中仍然有大量Excel 97-2003生成的.xls文件。这类文件用新版openpyxl不一定能友好读取用Pandas直接读也可能遇到类型问题而xlrd读取.xls、xlwt创建.xls刚好是传统技术栈中比较稳的组合。本文不会只说“能读能写”还会把open_workbook、add_sheet、write等关键函数参数以及日期单元格、合并单元格、重复写入报错等真实使用中容易踩的坑完整过一遍。如果你是Python初学者今天这篇文章可以当成Excel自动化的第一课如果你已经有Pandas或openpyxl经验也能通过对比快速了解旧版文件格式的工作方式。1. xlrd和xlwt核心能力速览先把结论放在前面xlrd和xlwt不是“万能的Excel库”而是针对旧版.xls格式的专业模块。核心能力说明模块作用xlrd负责读取Excel工作簿xlwt负责创建工作簿并写入数据文件类型主要处理Excel 97-2003的.xls文件读取能力工作表列表、行数、列数、单元格值、单元格类型、日期处理写入能力创建多张工作表、写入文本和数字、设置字体、对齐、边框、列宽常用入口xlrd.open_workbook()、workbook.sheet_by_index()、xlwt.Workbook()、sheet.write()是否需要GPU不需要是否支持批量任务支持通过循环处理多个.xls文件是否提供HTTP接口不提供需要自己封装函数或接入FastAPI/Flask适合场景读取历史.xls报表、批量汇总、生成简单.xls统计表不建议场景处理新版.xlsx、超大Excel、复杂图表、复杂数据透视表注意版本问题。xlrd在2.0版本之后明确不再支持.xlsx只保留.xls等旧格式能力xlwt也一直定位为写旧版.xls文件。如果你手上的文件明明是.xlsx不要硬套这两个库直接使用openpyxl或pandas读取会更合适。后面章节会专门说明这种格式差异带来的报错。2. 适用场景与使用边界很多文章只讲“能做什么”很少讲“什么时候别用”。这里先做一个详细的边界说明。先看适合的场景。第一类是企业历史数据归档。很多公司把十年的销售记录、库存流水、考勤记录存在.xls文件里文件命名混乱字段偶尔不一样。用xlrd可以把这些文件批量读出来整理成统一格式后再写回新文件。第二类是教学场景和小型工具开发。Python入门项目里经常需要做“成绩单生成器”“课表转换器”这类练习。xlwt的API足够简单写一个循环就能生成一张完整表格适合用来理解Excel文件的底层单元格模型。第三类是配合定时任务做自动化报表。每天从数据库或接口拉数据然后用xlwt生成一个固定格式的.xls文件再通过企业微信、钉钉或者邮件发送出去是很常见的自动化路径。不适合的场景也要意识到。如果你处理的是新版的.xlsx文件尤其包含图片、图表、数据透视表、复杂公式xlrd和xlwt并不合适。xlwt本身不执行公式计算它只能把Excel公式字符串写入单元格真正打开文件后由Excel重新计算也可能因为缺少计算引擎导致结果没有预先缓存。另外xlwt对Excel行数存在上限限制。旧版.xls格式的行上限是65536行如果业务数据量动辄几十万行就应该考虑CSV、数据库或Pandasopenpyxl方案而不是硬塞进.xls文件。最后是安全和隐私边界。如果你要读取他人提供的Excel文件尤其是包含工号、手机号、工资、客户联系信息的工作簿使用前要确认数据来源合规处理过程中尽量脱敏不要把完整文件直接发布到公开网络。批量处理脚本尤其要注意输出目录权限避免生成的文件被其他人随意访问。3. 环境准备安装xlrd和xlwt安装过程很简单。打开终端或命令提示符先确认Python可用。python --version python -m pip --version如果使用的是Windows并且只装了Python官方安装包的“py”启动器也可以这样检查py --version py -m pip --version然后安装两个库python -m pip install xlrd xlwt如果安装速度慢可以临时使用国内镜像源python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple xlrd xlwt安装完成后验证导入是否正常python -c import xlrd, xlwt; print(xlrd and xlwt import ok)看到输出没有报错说明环境已经准备好。这里提醒一句我给出的命令都假设你已经安装了Python并且pip可用。如果Python环境本身很乱建议优先使用虚拟环境或conda环境避免和其他项目依赖冲突。3.1 版本选择建议如果你想体验xlrd最完整的API并且要兼容一些老教程的写法可以固定安装1.2.0版本python -m pip install xlrd1.2.0xlrd 1.2.0还能读取部分xlsx文件而xlrd 2.0之后彻底移除了xlsx支持遇到xlsx会直接抛出“Excel xlsx file; not supported”之类的错误。这个错误信息非常常见不是你的代码写错而是版本定位改变导致。我的推荐是跟随你安装的Python版本去更新这些库先用最新版跑通核心代码。如果公司项目里本来就有旧版写法依赖再按项目要求锁定版本。4. 使用xlrd读取Excel核心方法与参数说明先创建一个读取流程打开文件、选择工作表、读取单元格、遍历行。需要特别说明xlrd不是把Excel文件直接转成DataFrame而是保留了工作簿、工作表、单元格这样的层级结构。读文件时先得到Workbook对象再从Workbook对象中取Sheet对象最后从Sheet对象中读取单元格数据。4.1 open_workbook读取工作簿下面这段代码是最基础的读取动作import xlrd # 打开xls文件当前目录下需要有这个文件 workbook xlrd.open_workbook(成绩表.xls) # 1. 打印所有工作表名称 print(workbook.sheet_names()) # 2. 按索引取第一张工作表 sheet workbook.sheet_by_index(0) # 3. 输出表名、行数、列数 print(工作表名称:, sheet.name) print(总行数:, sheet.nrows) print(总列数:, sheet.ncols) # 4. 逐行读取 for row in range(sheet.nrows): print(row, sheet.row_values(row))open_workbook()是xlrd的核心入口常用参数如下参数作用使用注意filename要读取的.xls文件路径如果传入.xlsx2.0版本以后会报错file_contents直接传入二进制文件内容适合网络请求或内存中读取formatting_info是否读取单元格格式信息对.xls有效会增加内存和加载时间on_demand是否按需加载工作表有助于减少加载大文件时的资源占用ragged_rows是否允许每行返回不同列数设置为True时空白行尾可能被裁剪不同行col取列可能不同formatting_infoTrue可以读取单元格格式但不代表你可以用xlrd“原样复制”所有样式到另一个文件。它主要用于检查原始文件里的格式信息后续写入样式仍需要xlwt重新设置。4.2 获取工作表的常用方法读取过程中真正频繁使用的是Sheet对象的方法和属性。操作方法或属性返回值说明获取所有工作表名workbook.sheet_names()返回工作表名称列表按索引取工作表workbook.sheet_by_index(0)返回Sheet对象按名称取工作表workbook.sheet_by_name(Sheet1)取不到名称时报错获取总行数sheet.nrows返回整数获取总列数sheet.ncols返回整数读取整行sheet.row_values(row_index)返回该行所有单元格的值列表读取整列sheet.col_values(col_index)返回该列所有单元格的值列表读取单个单元格sheet.cell_value(row_index, col_index)返回单元格的值读取单元格类型sheet.cell_type(row_index, col_index)返回单元格类型对应的数字下面这段代码展示了通过名称定位工作表和按列读取数据import xlrd workbook xlrd.open_workbook(学生信息.xls) sheet workbook.sheet_by_name(学生表) print(前两行数据) for row in range(2): print(sheet.row_values(row)) print(第一列数据) print(sheet.col_values(0)) # 读取单个单元格 cell_value sheet.cell_value(1, 0) cell_type sheet.cell_type(1, 0) print(第一个数据单元格的值:, cell_value) print(单元格类型编号:, cell_type)对xlrd来说单元格值可能是字符串、浮点数、日期、布尔值。读取后看到数字被转成float是正常现象因为这个库沿用Excel序列化方式很多整数在存储时本来就是浮点数。如果需要显示成整数可以在Python侧转换value sheet.cell_value(1, 1) if isinstance(value, float) and value.is_integer(): value int(value)5. 使用xlwt写入Excel核心方法实战xlwt写文件和xlrd读文件是镜像关系。先创建Workbook对象再add_sheet添加工作表通过sheet.write写入单元格最后save保存文件。5.1 创建第一张Excel表最基础的写入示例import xlwt # 创建工作簿明确使用utf-8编码 workbook xlwt.Workbook(encodingutf-8) # 添加工作表 sheet workbook.add_sheet(学生成绩) # 写入单元格write(行, 列, 值) sheet.write(0, 0, 姓名) sheet.write(0, 1, 语文) sheet.write(0, 2, 数学) sheet.write(0, 3, 总分) sheet.write(1, 0, 张三) sheet.write(1, 1, 92) sheet.write(1, 2, 90) sheet.write(1, 3, 182) # 保存文件 workbook.save(学生成绩.xls)运行后会在当前目录生成一个.xls文件用Excel打开能看到第一行是表头第二行是学生数据。5.2 Workbook和write方法参数说明先用参数表格理解xlwt的核心API。Workbook()的主要参数参数作用默认值encoding设置工作簿编码建议使用utf-8asciistyle_compression是否压缩样式数据0通常不需要改add_sheet()的参数参数作用说明sheetname工作表名称建议不超过31字符避免特殊字符cell_overwrite_ok是否允许重复覆盖单元格False时重复写同一个单元格会报错write()的参数参数作用说明r行索引从0开始c列索引从0开始label单元格内容可以是字符串、数字、日期style单元格样式默认是默认样式初学者最容易遇到的是“Attempt to overwrite cell”报错。原因是一个单元格被连续写两次。解决方式不是无脑打开cell_overwrite_ok而是检查代码结构避免同一个单元格被写到两次。推荐这类报错处理思路先看行循环和列循环的索引是否重复再确定是否需要覆盖。比如你要先写原始值后续又要写汇总值到同一位置那必须设计好写入顺序。5.3 带样式的表格写入示例只写纯文本容易但实际报表通常需要表头、边框、对齐。xlwt支持用easyxf快速定义样式。import xlwt workbook xlwt.Workbook(encodingutf-8) sheet workbook.add_sheet(成绩单) # 标题样式加粗、字号高一点、水平居中 title_style xlwt.easyxf( font: bold on, height 220; align: horizontal center; ) # 表头样式加粗、居中、带边框 header_style xlwt.easyxf( font: bold on; align: horizontal center; borders: bottom thin; ) # 普通内容样式居中字体使用默认 content_style xlwt.easyxf( align: horizontal center; ) # 第一行写入合并标题 sheet.write_merge(0, 0, 0, 3, 2026年春季期末成绩, title_style) # 第二行写入表头 headers [姓名, 语文, 数学, 总分] for col, text in enumerate(headers): sheet.write(1, col, text, header_style) # 后续行写入数据 rows [ [张三, 92, 90, 182], [李四, 88, 95, 183], [王五, 91, 98, 189], ] for row_index, row_data in enumerate(rows, start2): for col_index, value in enumerate(row_data): sheet.write(row_index, col_index, value, content_style) # 设置列宽单位约为字符宽度的256倍 sheet.col(0).width 256 * 12 sheet.col(1).width 256 * 10 sheet.col(2).width 256 * 10 sheet.col(3).width 256 * 10 workbook.save(成绩单.xls)这段代码中的write_merge(0, 0, 0, 3, text, style)表示把第0行到第0行、第0列到第3列合并成一个单元格通常用于设计报表主标题。5.4 将生成的xls重新读取验证写完文件后最好再用xlrd读一遍验证行数、表头和数据是否都在。import xlrd workbook xlrd.open_workbook(成绩单.xls) sheet workbook.sheet_by_index(0) print(工作表名称:, sheet.name) print(总行数:, sheet.nrows) print(总列数:, sheet.ncols) for row in range(sheet.nrows): print(row, sheet.row_values(row))预期输出中会看到第一行是合并标题“2026年春季期末成绩”第二行是表头后面三行是学生数据。数值列可能会以92.0这种浮点形式出现这是xlrd读取.xls时常见的表现不是写入错误。6. 日期判断与单元格类型处理实战Excel日期是很多自动化的隐藏难点。表面上Excel里写的可能是“2026-05-07”在xlrd底层看来却是一个序列化数字需要借助datemode转化后才能得到真正的Python日期对象。创建一个带日期的xls文件from datetime import date import xlwt workbook xlwt.Workbook(encodingutf-8) sheet workbook.add_sheet(日期示例) # 日期格式样式 date_style xlwt.easyxf(num_format_strYYYY-MM-DD) sheet.write(0, 0, 日期) sheet.write(0, 1, 说明) sheet.write(1, 0, date(2026, 5, 7), date_style) sheet.write(1, 1, 系统上线日) sheet.write(2, 0, date(2026, 5, 8), date_style) sheet.write(2, 1, 数据截止日) workbook.save(日期示例.xls)读取并转换日期import xlrd workbook xlrd.open_workbook(日期示例.xls) sheet workbook.sheet_by_index(0) print(Excel日期系统模式:, workbook.datemode) for row in range(sheet.nrows): value sheet.cell_value(row, 0) cell_type sheet.cell_type(row, 0) print(原始值:, value, 类型编号:, cell_type) # 转换第一行数据单元格的日期 cell_value sheet.cell_value(1, 0) datetime_obj xlrd.xldate_as_datetime(cell_value, workbook.datemode) print(转换后的日期:, datetime_obj.strftime(%Y-%m-%d))这里的关键方法是xlrd.xldate_as_datetime()它接收两个参数一个是单元格的数字值另一个是工作簿的日期模式。直接打印数字往往没有任何可读性必须经过转换。如果单元格里本来存的是字符串“2026-05-07”而不是Excel日期类型就不会触发日期转换逻辑读取结果就是一个普通字符串。判断一个单元格是不是日期可以通过sheet.cell_type(row, col)返回的结果判断常见的类型编号规则在旧版xlrd中会区分数字、日期、文本。由于不同版本常量位置略有差异实际项目中建议先用上面代码打印出类型编号再对照当前xlrd版本的处理逻辑不要直接照搬网上没有版本说明的魔法常量。7. 批量整理和写入多个xls文件单个文件处理是基础真正办公自动化中更常见的是多个xls文件需要批量汇总。xlrd和xlwt本身不提供“批量”按钮但我们可以用Python的循环把流程组合起来。7.1 批量读取目录中所有xls文件现在假设目录中有多个xls文件每个文件的第一行都是表头需要把所有数据行汇总到一个综合文件里。from pathlib import Path import xlrd import xlwt src_dir Path(./xls_files) output_file 汇总结果.xls # 汇总文件 summary_book xlwt.Workbook(encodingutf-8) summary_sheet summary_book.add_sheet(汇总) # 用来记录写到汇总文件中的行索引 write_row 0 # 只处理当前目录下的xls文件 for file_path in sorted(src_dir.glob(*.xls)): print(正在处理:, file_path.name) book xlrd.open_workbook(str(file_path)) sheet book.sheet_by_index(0) # 如果这是第一个文件把表头写进去 if write_row 0: headers sheet.row_values(0) for col, header in enumerate(headers): summary_sheet.write(write_row, col, header) write_row 1 # 从第1行开始读取数据跳过表头 for row_index in range(1, sheet.nrows): row_data sheet.row_values(row_index) for col_index, value in enumerate(row_data): summary_sheet.write(write_row, col_index, value) write_row 1 summary_book.save(output_file) print(汇总完成输出文件:, output_file)这个代码的核心有两点。第一点是目录扫描用的Path.glob(*.xls)可以帮助你批量获取文件列表第二点是写入时维护一个write_row游标避免不同文件的数据互相覆盖。如果要同时处理xls和xlsx混合目录建议分开处理。xlrd处理xlsopenpyxl处理xlsx最后把两组数据合并到同一个目标文件。另一个更稳妥的做法是先把所有文件转成标准行数据列表再统一写Excel逻辑分层会让排错更容易。7.2 处理单个异常文件不中断任务批量处理最大的问题是“一个文件出错整个程序崩溃”。真实文件里经常有隐藏的损坏文件、空工作表、非预期的数据类型批量脚本必须给每个文件加异常捕获。from pathlib import Path import xlrd import xlwt src_dir Path(./xls_files) output_file 有容错汇总.xls summary_book xlwt.Workbook(encodingutf-8) summary_sheet summary_book.add_sheet(Sheet1) write_row 0 error_files [] for file_path in sorted(src_dir.glob(*.xls)): try: book xlrd.open_workbook(str(file_path)) sheet book.sheet_by_index(0) if write_row 0: headers sheet.row_values(0) for col, header in enumerate(headers): summary_sheet.write(write_row, col, header) write_row 1 for row_index in range(1, sheet.nrows): row_data sheet.row_values(row_index) for col_index, value in enumerate(row_data): summary_sheet.write(write_row, col_index, value) write_row 1 except Exception as exc: error_files.append((file_path.name, str(exc))) summary_book.save(output_file) print(处理完成失败文件如下) for name, error in error_files: print(name, error)这种设计能够保证批量任务不会因为一个文件损坏就全部停摆。后续可以在error_files上增加重试机制、告警通知或单独把错误文件移动到error_dir目录。7.3 是否有接口API可以调用直接说结论xlrd和xlwt本身