🚀 告别手动导出!Python一键把50个Excel转成PDF报表,同事以为我开了外挂
💡 每月把几十个Excel另存为PDF发给客户?一个脚本3分钟搞定,省下的时间够摸一上午鱼 🐟
🎯 场景还原
小李是某公司的销售助理,每个月底要做这些事:
把30个区域的销售Excel,逐个打开 每个都要调整打印区域、页眉页脚 另存为PDF,发给对应的区域经理
每次光"打开→调格式→另存为PDF"就要点30×5=150次鼠标,加上等Excel响应的时间,2小时起步。
更要命的是,总有那么一两个会忘掉,第二天被领导问"XX区域的报表怎么没发?" 😅
如果你也干过这种活,今天这个脚本就是为你写的。
📦 用到的库
pip install pywin32 pandas openpyxl
| pywin32 | |
| pandas | |
| openpyxl |
💡 为什么用
pywin32而不是直接转PDF? 因为它是调用真实Excel程序去转换,图表、条件格式、打印区域全都能保留,效果跟手动"另存为PDF"一模一样!
⚠️ 注意:此方案仅适用于Windows系统(需要安装Microsoft Excel)
🔧 完整代码
import os
import sys
import time
defexcel_to_pdf_batch(input_folder, output_folder=None):
"""
批量将Excel文件转换为PDF
:param input_folder: Excel文件所在文件夹
:param output_folder: PDF输出文件夹(默认同目录下新建PDF文件夹)
"""
# 检查是否在Windows环境
if sys.platform != 'win32':
print("❌ 此脚本仅支持Windows系统(需要Microsoft Excel)")
return
try:
import win32com.client
except ImportError:
print("❌ 请先安装pywin32:pip install pywin32")
return
# 默认输出到同级目录的PDF文件夹
if output_folder isNone:
output_folder = os.path.join(input_folder, "PDF输出")
os.makedirs(output_folder, exist_ok=True)
# 收集所有Excel文件
excel_files = [
f for f in os.listdir(input_folder)
if f.lower().endswith(('.xlsx', '.xls')) andnot f.startswith('~$')
]
ifnot excel_files:
print(f"⚠️ 文件夹 {input_folder} 中没有找到Excel文件")
return
print(f"📂 找到 {len(excel_files)} 个Excel文件,开始转换...\n")
# 启动Excel程序(后台运行,不弹窗)
excel_app = win32com.client.DispatchEx("Excel.Application")
excel_app.Visible = False# 不显示Excel窗口
excel_app.DisplayAlerts = False# 不弹警告框
success_count = 0
fail_list = []
for i, filename in enumerate(excel_files, 1):
filepath = os.path.abspath(os.path.join(input_folder, filename))
pdf_name = os.path.splitext(filename)[0] + ".pdf"
pdf_path = os.path.abspath(os.path.join(output_folder, pdf_name))
try:
print(f" [{i}/{len(excel_files)}] 转换中:{filename}")
# 打开工作簿
wb = excel_app.Workbooks.Open(filepath)
# 设置打印区域为"已使用区域"
for ws in wb.Worksheets:
ws.PageSetup.Zoom = False
ws.PageSetup.FitToPagesWide = 1# 宽度适合1页
ws.PageSetup.FitToPagesTall = False# 高度不限
# 导出PDF(0 = xlTypePDF)
wb.ExportAsFixedFormat(0, pdf_path)
wb.Close(SaveChanges=False)
success_count += 1
print(f" ✅ 成功 → {pdf_name}")
except Exception as e:
fail_list.append((filename, str(e)))
print(f" ❌ 失败:{e}")
try:
wb.Close(SaveChanges=False)
except:
pass
# 关闭Excel程序
excel_app.Quit()
del excel_app
# 汇总结果
print(f"\n{'='*40}")
print(f"🎉 转换完成!")
print(f" ✅ 成功:{success_count} 个")
print(f" ❌ 失败:{len(fail_list)} 个")
print(f" 📁 PDF输出目录:{output_folder}")
if fail_list:
print(f"\n⚠️ 失败文件清单:")
for fname, err in fail_list:
print(f" - {fname}:{err}")
if __name__ == "__main__":
excel_to_pdf_batch(
input_folder=r"D:\销售报表\2026年5月", # 改成你的Excel文件夹
output_folder=r"D:\销售报表\2026年5月-PDF"# 改成你想输出的位置
)
📝 代码解读
核心原理只有3步:
启动Excel后台进程 → DispatchEx("Excel.Application")就像偷偷打开了一个Excel,但不让你看到窗口逐个打开文件,设置打印格式 → FitToPagesWide = 1让内容宽度适配一页PDF,不会被截断导出PDF → ExportAsFixedFormat(0, pdf_path)跟你手动点"另存为PDF"效果完全一样
关键细节:
~$开头的文件是Excel临时文件(打开时生成的),必须跳过Visible = False让Excel在后台运行,不干扰你干别的事DisplayAlerts = False屏蔽"是否保存"之类的弹窗
⚠️ 避坑指南
坑①:转换后PDF内容被截断/排版乱掉
现象:转出来的PDF,表格右边被切掉了,或者挤成一团
原因:Excel的打印区域没有正确设置,默认只打印"已使用区域",但有些单元格虽然有内容却不被识别
修复方法:
# 在ExportAsFixedFormat之前,手动设置打印区域
for ws in wb.Worksheets:
used_range = ws.UsedRange
last_row = used_range.Row + used_range.Rows.Count - 1
last_col = used_range.Column + used_range.Columns.Count - 1
first_cell = ws.Cells(1, 1).Address
last_cell = ws.Cells(last_row, last_col).Address
ws.PageSetup.PrintArea = f"{first_cell}:{last_cell}"
坑②:脚本运行后Excel进程没退出,占内存
现象:任务管理器里多了几十个 EXCEL.EXE,电脑越来越卡
原因:如果中途报错,excel_app.Quit() 可能没被执行到
修复方法:用 try...finally 确保退出:
excel_app = win32com.client.DispatchEx("Excel.Application")
try:
# ... 所有转换逻辑 ...
finally:
excel_app.Quit()
del excel_app # 彻底释放COM对象
坑③:中文路径或文件名报错
现象:文件路径含中文时,ExportAsFixedFormat 直接报错
原因:COM接口对中文路径支持不稳定
修复方法:用 os.path.abspath() 转换为绝对路径(代码中已包含),如果还报错,尝试把文件复制到纯英文路径再转换。
💡 扩展思路
自动发送邮件 → 结合 smtplib,转完PDF自动发给对应区域经理,实现"一键到底"加水印/页码 → 用 PyPDF2或reportlab在PDF上叠加公司水印和页码定时执行 → 用Windows任务计划程序,每月最后一天自动运行,彻底解放双手
📢 涨粉引导区
🎁 关注「码上办公」回复「Python」获取:
50个办公自动化脚本合集 Python快捷键速查表 本文完整源码+测试数据 💬 思考题:如果每个Excel文件里有多个Sheet,但你只想转其中名为"汇总"的Sheet为PDF,代码应该怎么改?评论区见~
👉 觉得有用?点「在看」+「收藏」,下次用到不迷路!
夜雨聆风