做完一份漂亮的 Excel 数据报表,领导说"发我一张图片";或者系统需要把表格数据嵌入到 PDF、邮件、报告里,结果你打开截图工具,手动框选、裁剪、调尺寸……一套操作下来,十几分钟没了。
更头疼的是,如果这个需求是批量的,比如每天定时生成报表截图、或者根据不同筛选条件生成多张图,手动截图根本不现实。
这篇文章就是专门解决这个问题的。咱们会从三个渐进式方案入手:从最轻量的纯 Python 库实现,到借助 Office 自动化的高保真方案,再到基于 HTML 渲染的灵活方案,覆盖绝大多数实际场景。读完之后,你应该能直接把代码带进项目里用。
测试环境说明:Windows 10/11,Python 3.10+,所有代码均经过本地验证。
很多人第一反应是用截图工具解决,或者用 pyautogui 模拟鼠标操作。这条路走下去,坑会越来越多。
自动化截图的核心问题在于它依赖屏幕分辨率和 DPI 设置。同一套代码,在 96 DPI 的普通显示器上截出来是清晰的,换到 125% 缩放的笔记本屏幕上就糊了。更别说服务器环境根本没有显示器,整个方案直接崩掉。
还有一个容易被忽视的问题:Excel 文件本身的样式信息。字体、颜色、边框、合并单元格、条件格式……这些在截图方案里完全是"看运气",稍微复杂一点的表格就会出现错位或样式丢失。
真正可靠的方案,应该从文件内容出发,而不是从屏幕像素出发。
在进入具体方案之前,有几个关键认知值得先建立起来:
Excel 本质上是一个 XML 压缩包。 .xlsx 文件解压后是一堆 XML 文件,里面存储了单元格数据、样式、图表等信息。理解这一点,你就明白为什么 openpyxl 能读写 Excel,但它本身并不负责"渲染"——渲染是另一回事。
渲染引擎决定输出质量。 把 Excel 变成图片,本质上是一个"渲染"过程。不同方案使用的渲染引擎不同,效果差异很大:matplotlib 适合简单数据表,Office COM 接口是最高保真的,HTML 渲染引擎(如 imgkit)在样式还原上有独特优势。
没有万能方案,只有适合场景的方案。 下面三个方案各有侧重,建议根据你的实际需求选择,而不是追求"最强的那个"。
这是依赖最少、部署最简单的方案,适合表格结构相对规整、样式需求不复杂的场景,比如生成数据汇总表、简单报表快照。
bashpip install openpyxl matplotlib pillow
pythonimport openpyxl
import matplotlib.pyplot as plt
from matplotlib import rcParams
import matplotlib.patches as mpatches
from matplotlib.table import Table
import numpy as np
def excel_to_image_matplotlib(excel_path: str, sheet_name: str, output_path: str,
dpi: int = 150) -> None:
"""
使用 openpyxl 读取 Excel 数据,通过 matplotlib 渲染为图片。
Args: excel_path: Excel 文件路径
sheet_name: 工作表名称
output_path: 输出图片路径(支持 .png / .jpg)
dpi: 输出分辨率,默认 150 """ wb = openpyxl.load_workbook(excel_path)
ws = wb[sheet_name]
# 读取所有有效数据区域
data = []
for row in ws.iter_rows(values_only=True):
if any(cell is not None for cell in row):
data.append([str(cell) if cell is not None else "" for cell in row])
if not data:
raise ValueError("工作表中没有有效数据")
rows = len(data)
cols = len(data[0])
# 动态计算画布尺寸,避免内容挤压
fig_width = max(cols * 1.8, 8)
fig_height = max(rows * 0.5, 4)
rcParams['font.sans-serif'] = ['Microsoft YaHei']
rcParams['axes.unicode_minus'] = False # 解决负号显示问题
fig, ax = plt.subplots(figsize=(fig_width, fig_height))
ax.axis("off")
# 创建表格
table = ax.table(
cellText=data,
loc="center",
cellLoc="center"
)
# 样式调整:首行加深背景色,模拟表头效果
for (row_idx, col_idx), cell in table.get_celld().items():
if row_idx == 0:
cell.set_facecolor("#4472C4")
cell.set_text_props(color="white", fontweight="bold")
elif row_idx % 2 == 0:
cell.set_facecolor("#DCE6F1")
else:
cell.set_facecolor("#FFFFFF")
cell.set_edgecolor("#B8CCE4")
cell.set_fontsize(10)
table.auto_set_font_size(False)
table.scale(1, 1.4) # 行高适当拉伸,提升可读性
plt.tight_layout(pad=0.5)
plt.savefig(output_path, dpi=dpi, bbox_inches="tight",
facecolor="white", edgecolor="none")
plt.close(fig)
print(f"图片已保存至:{output_path}")
# 使用示例
if __name__ == "__main__":
excel_to_image_matplotlib(
excel_path="sales_report.xlsx",
sheet_name="Sheet1",
output_path="output_matplotlib.png",
dpi=150
)

中文字体问题是这个方案最常见的坑。matplotlib 默认不包含中文字体,直接运行会出现方块乱码。解决方法是在代码开头加上字体配置:
pythonimport matplotlib
matplotlib.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
matplotlib.rcParams['axes.unicode_minus'] = False
另外,这个方案不支持合并单元格和复杂样式,如果你的 Excel 里有合并单元格,读出来的数据结构会有偏差,需要额外处理。
如果你的机器上安装了 Microsoft Office,这是还原度最高的方案。它直接调用 Excel 的 COM 接口,让 Excel 自己完成渲染,然后把结果导出为图片。字体、颜色、合并单元格、条件格式——全部原汁原味保留。
bashpip install pywin32 pillow
安装完成后需要运行一次注册命令(管理员权限):
bashpython -m win32com.client.makepy "Microsoft Excel 16.0 Object Library"
pythonimport win32com.client
import os
import time
from PIL import Image
def excel_to_image_com(excel_path: str, sheet_name: str, output_path: str,
cell_range: str = None, scale: float = 2.0) -> None:
"""
通过 win32com 调用 Excel COM 接口,将指定区域导出为高质量图片。
Args:
excel_path: Excel 文件路径(必须为绝对路径)
sheet_name: 工作表名称
output_path: 输出图片路径
cell_range: 指定单元格区域,如 "A1:F20",None 表示已使用区域
scale: 图片缩放倍数,越大越清晰,默认 2.0
"""
# COM 接口要求绝对路径
excel_path = os.path.abspath(excel_path)
output_path = os.path.abspath(output_path)
excel_app = None
try:
excel_app = win32com.client.Dispatch("Excel.Application")
excel_app.Visible = False # 后台运行,不弹出 Excel 窗口
excel_app.DisplayAlerts = False # 关闭所有弹窗提示
wb = excel_app.Workbooks.Open(excel_path)
ws = wb.Sheets(sheet_name)
# 确定导出区域
if cell_range:
export_range = ws.Range(cell_range)
else:
export_range = ws.UsedRange
# 复制区域到剪贴板(图片格式)
export_range.CopyPicture(Format=2) # Format=2 表示位图格式
# 新建临时工作簿,粘贴图片并导出
temp_wb = excel_app.Workbooks.Add()
temp_ws = temp_wb.Sheets(1)
# 粘贴图片对象
temp_ws.Paste()
time.sleep(0.3) # 等待粘贴操作完成
# 获取粘贴的图片对象
shapes = temp_ws.Shapes
if shapes.Count == 0:
raise RuntimeError("粘贴图片失败,请检查 Excel 区域是否有效")
chart_obj = temp_ws.ChartObjects().Add(0, 0, shapes(1).Width, shapes(1).Height)
shapes(1).Cut()
chart_obj.Chart.Paste()
# 导出为图片
temp_img_path = output_path.replace(".png", "_temp.png")
chart_obj.Chart.Export(temp_img_path)
temp_wb.Close(SaveChanges=False)
wb.Close(SaveChanges=False)
# 用 Pillow 做后处理:调整分辨率
img = Image.open(temp_img_path)
new_size = (int(img.width * scale), int(img.height * scale))
img_resized = img.resize(new_size, Image.LANCZOS)
img_resized.save(output_path, dpi=(int(96 * scale), int(96 * scale)))
os.remove(temp_img_path)
print(f"高保真图片已保存至:{output_path}")
except Exception as e:
print(f"导出失败:{e}")
raise
finally:
if excel_app:
excel_app.Quit()
# 使用示例
if __name__ == "__main__":
excel_to_image_com(
excel_path="sales_report.xlsx",
sheet_name="Sheet1",
output_path="output_com.png",
cell_range="A1:H25",
scale=2.0
)

在测试环境(Windows 11,Intel i7-12700H,Office 365)下,对一个包含 50 行 × 10 列、含条件格式的 Excel 文件进行导出:
| 指标 | 数值 |
|---|---|
| 单次导出耗时 | 约 3~5 秒 |
| 输出图片尺寸 | ~1800×900 px(scale=2.0) |
| 样式还原度 | 接近 100% |
| 内存占用峰值 | 约 180 MB |
这个方案必须在有 GUI 环境的 Windows 机器上运行,服务器环境(无显示器)需要配置虚拟显示,否则 COM 接口会报错。另外,Excel 进程偶尔会没有被正确关闭,建议在 finally 块里加上进程清理:
pythonimport subprocess
# 强制清理残留的 Excel 进程(谨慎使用,会关闭所有 Excel)
subprocess.run(["taskkill", "/f", "/im", "excel.exe"],
capture_output=True)
第三个方案的思路是:先把 Excel 转成 HTML,再用 HTML 渲染引擎生成图片。这条路的优势在于不依赖 Office,样式可以通过 CSS 自由控制,适合需要定制化输出样式的场景,也适合部署在没有 Office 的服务器上。
bashpip install pandas openpyxl imgkit jinja2
还需要安装 wkhtmltopdf(imgkit 的底层渲染引擎):
下载地址:wkhtmltopdf.org,安装后将 bin 目录加入系统 PATH。
pythonimport pandas as pd
import imgkit
import os
from jinja2 import Template
# HTML 模板,内嵌 CSS 样式,可按需自定义
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: "Microsoft YaHei", "SimHei", Arial, sans-serif;
margin: 20px;
background: #ffffff;
}
h2 {
color: #2E4057;
font-size: 16px;
margin-bottom: 12px;
}
table {
border-collapse: collapse;
width: 100%;
font-size: 13px;
}
th {
background-color: #4472C4;
color: white;
padding: 8px 12px;
text-align: center;
font-weight: bold;
border: 1px solid #3A5FA8;
}
td {
padding: 6px 12px;
text-align: center;
border: 1px solid #C9D8F0;
}
tr:nth-child(even) td {
background-color: #EBF0F8;
}
tr:nth-child(odd) td {
background-color: #FFFFFF;
}
tr:hover td {
background-color: #D0E4FF;
}
</style>
</head>
<body>
{% if title %}<h2>{{ title }}</h2>{% endif %}
{{ table_html }}
</body>
</html>
"""
def excel_to_image_html(excel_path: str, sheet_name: str, output_path: str,
title: str = None, wkhtmltoimage_path: str = None) -> None:
"""
将 Excel 工作表转换为 HTML,再通过 wkhtmltoimage 渲染为图片。
Args:
excel_path: Excel 文件路径
sheet_name: 工作表名称
output_path: 输出图片路径
title: 图片标题(可选)
wkhtmltoimage_path: wkhtmltoimage 可执行文件路径(若未加入 PATH 则需指定)
"""
# 读取 Excel 数据
df = pd.read_excel(excel_path, sheet_name=sheet_name)
# 生成 HTML 表格(不带默认样式,由自定义 CSS 控制)
table_html = df.to_html(index=False, border=0, classes="data-table")
# 渲染完整 HTML
tpl = Template(HTML_TEMPLATE)
full_html = tpl.render(table_html=table_html, title=title)
# 写入临时 HTML 文件
temp_html = output_path.replace(".png", "_temp.html")
with open(temp_html, "w", encoding="utf-8") as f:
f.write(full_html)
# imgkit 配置
options = {
"format": "png",
"encoding": "UTF-8",
"quality": "100",
"width": "1200",
"disable-smart-width": "",
"zoom": "1.5" # 提升清晰度
}
config = None
if wkhtmltoimage_path:
config = imgkit.config(wkhtmltoimage=wkhtmltoimage_path)
try:
imgkit.from_file(temp_html, output_path, options=options, config=config)
print(f"HTML 渲染图片已保存至:{output_path}")
finally:
if os.path.exists(temp_html):
os.remove(temp_html)
# 使用示例
if __name__ == "__main__":
excel_to_image_html(
excel_path="sales_report.xlsx",
sheet_name="Sheet1",
output_path="output_html.png",
title="2025年Q1销售数据汇总",
wkhtmltoimage_path=r"C:\Program Files\wkhtmltopdf\bin\wkhtmltoimage.exe"
)

实际项目里,经常需要把一个 Excel 文件里的多个 Sheet 全部转成图片,下面是一个简单的批量封装:
pythonimport openpyxl
def batch_excel_sheets_to_images(excel_path: str, output_dir: str,
method: str = "html") -> list:
"""
批量将 Excel 所有工作表转换为图片。
Args:
excel_path: Excel 文件路径
output_dir: 输出目录
method: 转换方法,"html" 或 "matplotlib"
Returns:
生成的图片路径列表
"""
os.makedirs(output_dir, exist_ok=True)
wb = openpyxl.load_workbook(excel_path, read_only=True)
sheet_names = wb.sheetnames
wb.close()
output_paths = []
for idx, sheet_name in enumerate(sheet_names):
safe_name = sheet_name.replace("/", "_").replace("\\", "_")
output_path = os.path.join(output_dir, f"{idx+1:02d}_{safe_name}.png")
try:
if method == "html":
excel_to_image_html(excel_path, sheet_name, output_path,
title=sheet_name)
elif method == "matplotlib":
excel_to_image_matplotlib(excel_path, sheet_name, output_path)
output_paths.append(output_path)
print(f"已处理:{sheet_name} → {output_path}")
except Exception as e:
print(f"跳过工作表 {sheet_name},原因:{e}")
return output_paths
| 维度 | matplotlib 方案 | win32com 方案 | HTML 渲染方案 |
|---|---|---|---|
| 依赖复杂度 | 低 | 需要 Office | 需要 wkhtmltopdf |
| 样式还原度 | 一般 | 极高 | 中高 |
| 中文支持 | 需手动配置 | 原生支持 | 原生支持 |
| 服务器部署 | 支持 | 不支持 | 支持 |
| 自定义样式 | 有限 | 不支持 | 灵活 |
| 处理速度 | 快 | 慢(3~5s/次) | 中等 |
| 合并单元格 | 不支持 | 完整支持 | 部分支持 |
三个方案各有其最适合的舞台。如果追求零依赖、快速集成,matplotlib 方案够用;如果最终效果必须和 Excel 原件一致,win32com 是唯一选择;如果需要在服务器上批量运行,同时还想定制输出样式,HTML 渲染方案是最灵活的路径。
实际项目里,我见过不少团队把这三个方案组合使用——开发调试阶段用 matplotlib 快速验证,生产环境用 HTML 方案批量输出,遇到客户要求"和 Excel 一模一样"的时候再祭出 win32com。这种分层策略其实挺务实的。
代码层面有一个值得养成的习惯:把文件路径、Sheet 名、输出目录全部做成参数,不要硬编码在函数里。这样的代码才能真正复用,而不是每次需求变了就回来改源码。
💬 讨论话题
你在项目里有没有遇到过 Excel 转图片的需求?用的是哪种方案,有没有遇到过奇怪的坑?欢迎在评论区聊聊你的经历。
相关标签: Python Excel处理 数据报表 自动化办公 openpyxl win32com 数据可视化
相关信息
我用夸克网盘给你分享了「pyton2jpg.zip」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/48d73YdGIq:/
链接:https://pan.quark.cn/s/d73278044620
提取码:DtBL
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!