当前位置:首页>python>Python PDF 自动化保姆级⑤:用代码自动生成业务报告,表格图表全搞定

Python PDF 自动化保姆级⑤:用代码自动生成业务报告,表格图表全搞定

  • 2026-10-15 03:00:28
Python PDF 自动化保姆级⑤:用代码自动生成业务报告,表格图表全搞定

前面四篇都是 "处理已有的 PDF",今天换个方向:从零生成 PDF。

职场中最痛苦的事情之一,就是每个月 / 每周写业务报告。数据从 Excel 里复制出来,粘贴到 Word,调整格式,插入图表,导出 PDF…… 一套流程下来两小时没了,而且每个月都要重复。

如果用 Python 自动生成呢?数据读取→表格生成→图表绘制→排版导出,全流程自动化。每月跑一次脚本,报告自动生成,你只需要检查一下就能发。

01 reportlab 是什么

reportlab 是 Python 最强大的 PDF 生成库,没有之一。它能做的事情包括:

  • 添加文字、段落、标题(支持中文)
  • 绘制表格(支持合并单元格、自定义样式)
  • 插入图片(PNG/JPG 等)
  • 绘制图表(柱状图、折线图、饼图等)
  • 页面布局(页眉页脚、分页、多栏)

安装:

pip install reportlab
reportlab 有两种使用方式:

低层 API(Canvas):像画笔一样在 PDF 页面上精确绘制每个元素,灵活但繁琐,适合精确排版。

高层 API(Platypus):用 "流式布局" 的方式,把段落、表格、图片作为 "元素" 依次放入文档,自动分页和排版,适合生成报告。

本文主要用 Platypus 高层 API,生成业务报告更高效。

02 第一个 PDF:Hello World

from reportlab.lib.pagesizes import A4from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacerfrom reportlab.lib.styles import getSampleStyleSheetfrom reportlab.lib.units import cm# 创建文档doc = SimpleDocTemplate("hello.pdf", pagesize=A4)# 获取样式styles = getSampleStyleSheet()# 构建内容列表story = []# 添加标题story.append(Paragraph("2024年Q3业务报告", styles["Title"]))story.append(Spacer(1, 0.5 * cm))# 添加正文段落story.append(Paragraph("本报告汇总了2024年第三季度的核心业务数据,包括销售业绩、客户增长和运营效率等关键指标。", styles["Normal"]))story.append(Spacer(1, 0.3 * cm))story.append(Paragraph("报告数据来源于公司CRM系统和财务系统,统计周期为2024年7月1日至9月30日。", styles["Normal"]))# 生成PDFdoc.build(story)print("PDF生成成功: hello.pdf")

核心概念:

  1. SimpleDocTemplate:文档模板,负责页面设置和整体布局。
  2. story:内容列表,按顺序存放所有元素(段落、表格、图片等),最后用 doc.build(story) 一次性生成。
  3. Paragraph:段落元素,支持 HTML 标签(如 <b>加粗</b>、<i>斜体</i>)。
  4. Spacer:空白间隔,用于控制元素间距。
  5. styles:样式集合,reportlab 自带了 Title、Heading1、Normal 等预设样式。

03 中文支持:注册中文字体

reportlab 默认不支持中文,直接写中文会显示为方框。需要注册中文字体:

from reportlab.lib.pagesizes import A4from reportlab.platypus import SimpleDocTemplate, Paragraphfrom reportlab.lib.styles import getSampleStyleSheet, ParagraphStylefrom reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFont# 注册中文字体(Windows示例)pdfmetrics.registerFont(TTFont("SimSun", "C:/Windows/Fonts/simsun.ttc"))pdfmetrics.registerFont(TTFont("SimHei", "C:/Windows/Fonts/simhei.ttf"))# 创建自定义样式styles = getSampleStyleSheet()title_style = ParagraphStyle(    "ChineseTitle",    parent=styles["Title"],    fontName="SimHei",    fontSize=20,    leading=28,    alignment=1,  # 居中)body_style = ParagraphStyle(    "ChineseBody",    parent=styles["Normal"],    fontName="SimSun",    fontSize=11,    leading=18,    firstLineIndent=22,  # 首行缩进)# 使用doc = SimpleDocTemplate("chinese.pdf", pagesize=A4)story = [    Paragraph("2024年第三季度业务报告", title_style),    Paragraph("本报告汇总了2024年第三季度的核心业务数据……", body_style),]doc.build(story)print("中文PDF生成成功")

各系统字体路径:

  • Windows:C:/Windows/Fonts/simsun.ttc(宋体)、simhei.ttf(黑体)
  • macOS:/System/Library/Fonts/PingFang.ttc(苹方)
  • Linux:/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc(文泉驿,需先安装)

04 绘制表格

表格是业务报告的核心元素。reportlab 的 Table 功能非常强大:

from reportlab.lib.pagesizes import A4from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacerfrom reportlab.lib.styles import getSampleStyleSheet, ParagraphStylefrom reportlab.lib import colorsfrom reportlab.lib.units import cmfrom reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFont# 注册字体(同上,省略)pdfmetrics.registerFont(TTFont("SimHei", "C:/Windows/Fonts/simhei.ttf"))pdfmetrics.registerFont(TTFont("SimSun", "C:/Windows/Fonts/simsun.ttc"))styles = getSampleStyleSheet()cell_style = ParagraphStyle("Cell", fontName="SimSun", fontSize=10, leading=14, alignment=1)header_style = ParagraphStyle("Header", fontName="SimHei", fontSize=10, leading=14, alignment=1, textColor=colors.white)# 表格数据data = [    [Paragraph("指标", header_style), Paragraph("Q1", header_style), Paragraph("Q2", header_style), Paragraph("Q3", header_style), Paragraph("环比", header_style)],    [Paragraph("销售额(万元)", cell_style), Paragraph("1,250", cell_style), Paragraph("1,480", cell_style), Paragraph("1,720", cell_style), Paragraph("+16.2%", cell_style)],    [Paragraph("客户数", cell_style), Paragraph("320", cell_style), Paragraph("385", cell_style), Paragraph("452", cell_style), Paragraph("+17.4%", cell_style)],    [Paragraph("客单价(元)", cell_style), Paragraph("390", cell_style), Paragraph("384", cell_style), Paragraph("380", cell_style), Paragraph("-1.0%", cell_style)],    [Paragraph("复购率", cell_style), Paragraph("28%", cell_style), Paragraph("31%", cell_style), Paragraph("35%", cell_style), Paragraph("+4pp", cell_style)],]# 创建表格table = Table(data, colWidths=[4*cm, 2.5*cm, 2.5*cm, 2.5*cm, 2.5*cm])# 设置样式table.setStyle(TableStyle([    # 表头背景色    ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#2E5BFF")),    # 表格边框    ("GRID", (0, 0), (-1, -1), 0.5, colors.grey),    # 交替行背景色    ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor("#F5F7FA")]),    # 内边距    ("TOPPADDING", (0, 0), (-1, -1), 8),    ("BOTTOMPADDING", (0, 0), (-1, -1), 8),    # 垂直居中    ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),]))# 生成PDFdoc = SimpleDocTemplate("table_report.pdf", pagesize=A4)story = [    Paragraph("核心指标汇总", ParagraphStyle("H", fontName="SimHei", fontSize=14, leading=20)),    Spacer(1, 0.5*cm),    table,]doc.build(story)print("表格PDF生成成功")

TableStyle 常用参数:

  • BACKGROUND
    :背景色,参数为 (起始列,起始行,结束列,结束行,颜色)
  • GRID
    :网格线,参数为 (范围,线宽,颜色)
  • ROWBACKGROUNDS
    :交替行背景色
  • SPAN
    :合并单元格,如 ("SPAN", (0,0), (1,0)) 合并前两列的第一行
  • FONTNAME
    、FONTSIZE:字体和字号
  • ALIGN
    、VALIGN:水平 / 垂直对齐

05 插入图表

推荐方案:用 matplotlib 生成图表再插入 PDF

reportlab 自带的图表功能有限,样式不够美观。更实用的方案是用 matplotlib 生成精美的图表,保存为图片,再插入 PDF:

import matplotlib.pyplot as pltimport matplotlibmatplotlib.rcParams["font.sans-serif"] = ["SimHei"]matplotlib.rcParams["axes.unicode_minus"] = False# 生成柱状图quarters = ["Q1", "Q2", "Q3", "Q4"]sales = [1250, 1480, 1720, 1890]plt.figure(figsize=(8, 4))plt.bar(quarters, sales, color="#2E5BFF")plt.title("季度销售额趋势", fontsize=14)plt.ylabel("销售额(万元)")plt.grid(axis="y", alpha=0.3)plt.tight_layout()plt.savefig("sales_chart.png", dpi=150)plt.close()# 插入PDFfrom reportlab.platypus import Imagefrom reportlab.lib.units import cmimg = Image("sales_chart.png", width=14*cm, height=7*cm)story.append(img)

这个方案的优势是 matplotlib 的图表样式更丰富、更美观,而且你可以完全控制图表的每一个细节。

06 完整业务报告生成示例

现在把上面的知识点整合起来,生成一份完整的业务报告:

from reportlab.lib.pagesizes import A4from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, PageBreakfrom reportlab.lib.styles import ParagraphStylefrom reportlab.lib import colorsfrom reportlab.lib.units import cmfrom reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFont# 注册字体pdfmetrics.registerFont(TTFont("SimHei", "C:/Windows/Fonts/simhei.ttf"))pdfmetrics.registerFont(TTFont("SimSun", "C:/Windows/Fonts/simsun.ttc"))# 样式定义title_style = ParagraphStyle("Title", fontName="SimHei", fontSize=22, leading=30, alignment=1, spaceAfter=20)h1_style = ParagraphStyle("H1", fontName="SimHei", fontSize=16, leading=24, spaceBefore=15, spaceAfter=10)h2_style = ParagraphStyle("H2", fontName="SimHei", fontSize=13, leading=20, spaceBefore=10, spaceAfter=8)body_style = ParagraphStyle("Body", fontName="SimSun", fontSize=11, leading=18, firstLineIndent=22)cell_style = ParagraphStyle("Cell", fontName="SimSun", fontSize=10, leading=14, alignment=1)header_style = ParagraphStyle("Header", fontName="SimHei", fontSize=10, leading=14, alignment=1, textColor=colors.white)# 构建报告doc = SimpleDocTemplate("business_report.pdf", pagesize=A4, topMargin=2*cm, bottomMargin=2*cm)story = []# 封面标题story.append(Spacer(1, 3*cm))story.append(Paragraph("2024年第三季度业务报告", title_style))story.append(Spacer(1, 1*cm))story.append(Paragraph("报告周期:2024年7月1日 - 9月30日", ParagraphStyle("Sub", fontName="SimSun", fontSize=12, alignment=1)))story.append(PageBreak())# 第一章:概述story.append(Paragraph("一、报告概述", h1_style))story.append(Paragraph("2024年第三季度,公司整体业务保持稳健增长。核心指标方面,销售额达到1,720万元,环比增长16.2%;客户总数达到452家,环比增长17.4%;复购率提升至35%,客户粘性持续增强。", body_style))story.append(Spacer(1, 0.3*cm))# 第二章:核心指标story.append(Paragraph("二、核心指标汇总", h1_style))story.append(Spacer(1, 0.3*cm))data = [    [Paragraph("指标", header_style), Paragraph("Q1", header_style), Paragraph("Q2", header_style), Paragraph("Q3", header_style), Paragraph("环比", header_style)],    [Paragraph("销售额(万元)", cell_style), Paragraph("1,250", cell_style), Paragraph("1,480", cell_style), Paragraph("1,720", cell_style), Paragraph("+16.2%", cell_style)],    [Paragraph("客户数", cell_style), Paragraph("320", cell_style), Paragraph("385", cell_style), Paragraph("452", cell_style), Paragraph("+17.4%", cell_style)],    [Paragraph("客单价(元)", cell_style), Paragraph("390", cell_style), Paragraph("384", cell_style), Paragraph("380", cell_style), Paragraph("-1.0%", cell_style)],    [Paragraph("复购率", cell_style), Paragraph("28%", cell_style), Paragraph("31%", cell_style), Paragraph("35%", cell_style), Paragraph("+4pp", cell_style)],]table = Table(data, colWidths=[4*cm, 2.5*cm, 2.5*cm, 2.5*cm, 2.5*cm])table.setStyle(TableStyle([    ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#2E5BFF")),    ("GRID", (0, 0), (-1, -1), 0.5, colors.grey),    ("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, colors.HexColor("#F5F7FA")]),    ("TOPPADDING", (0, 0), (-1, -1), 8),    ("BOTTOMPADDING", (0, 0), (-1, -1), 8),    ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),]))story.append(table)# 插入图表(假设已用matplotlib生成)story.append(Spacer(1, 0.5*cm))story.append(Paragraph("季度销售额趋势图", h2_style))story.append(Image("sales_chart.png", width=14*cm, height=7*cm))# 生成doc.build(story)print("完整业务报告生成成功: business_report.pdf")

07 页眉页脚和页码

专业报告通常需要页眉页脚和页码。用 onFirstPage 和 onLaterPages 回调函数实现:

from reportlab.lib.pagesizes import A4from reportlab.platypus import SimpleDocTemplate, Paragraphfrom reportlab.lib.units import cmdef add_header_footer(canvas, doc):    canvas.saveState()    # 页眉    canvas.setFont("SimHei", 9)    canvas.drawString(2*cm, A4[1] - 1.5*cm, "2024年Q3业务报告")    canvas.drawRightString(A4[0] - 2*cm, A4[1] - 1.5*cm, "机密文件")    # 页眉线    canvas.setStrokeColorRGB(0.5, 0.5, 0.5)    canvas.line(2*cm, A4[1] - 1.7*cm, A4[0] - 2*cm, A4[1] - 1.7*cm)    # 页脚页码    canvas.setFont("SimSun", 9)    page_num = canvas.getPageNumber()    canvas.drawCentredString(A4[0]/2, 1.2*cm, f"- {page_num} -")    canvas.restoreState()doc = SimpleDocTemplate(    "report_with_header.pdf",    pagesize=A4,    topMargin=2.5*cm,    bottomMargin=2*cm,)doc.build(story, onFirstPage=add_header_footer, onLaterPages=add_header_footer)

08 常见坑与解决方案

坑 1:中文显示为方框

必须注册中文字体,且所有用到中文的样式都要指定 fontName 为注册的中文字体。别忘了图表中的中文也要设置字体。

坑 2:表格内容溢出

如果单元格内容太长,会溢出表格。解决方法:1)用 Paragraph 包裹单元格内容,支持自动换行;2)调整列宽;3)缩小字号。

坑 3:图片模糊

插入的图片分辨率不够会模糊。用 matplotlib 生成图表时设置 dpi=150 或更高。插入图片时按比例缩放,不要拉伸变形。

坑 4:分页时表格被截断

长表格跨页时,reportlab 默认会把表格整体放在一页,放不下就会报错或留白。解决方法:使用 LongTable 替代 Table,它支持自动跨页。或者把大表格拆成多个小表格。

写在最后

用 reportlab 生成 PDF 业务报告,前期需要花时间搭建模板和样式,但一旦模板建好,后续每月只需要更新数据,跑一下脚本就能生成一份格式统一、排版精美的报告。这就是自动化的价值:把重复劳动交给代码,把创造性工作留给自己。

最新文章

随机文章