Python從零構(gòu)建一個自動化報表生成器
引言:告別手動報表,擁抱自動化
在日常工作中,報表生成是一項頻繁且繁瑣的任務(wù)。作為數(shù)據(jù)分析師、運營或財務(wù)人員,你可能每周甚至每天都需要從數(shù)據(jù)庫或CSV中提取原始數(shù)據(jù),用Excel進行清洗、計算,然后制作成格式統(tǒng)一的報表,最后通過郵件發(fā)送給相關(guān)人員。這個過程重復(fù)、耗時,且容易出錯。
在“Python辦公自動化”學(xué)習(xí)路徑的第四階段(第36-50天),我們的目標是讓機器人學(xué)會“讀寫算”——能夠處理Excel、PDF、CSV等常見辦公文檔。今天,我們將迎來本階段的壓軸實戰(zhàn)項目:構(gòu)建一個自動化報表生成器。這個項目將整合數(shù)據(jù)讀取、清洗分析、Excel報表生成、PDF轉(zhuǎn)換以及郵件發(fā)送的全流程,讓你親身體驗從原始數(shù)據(jù)到最終郵件的“一鍵式”自動化。
通過本文,你將掌握:
- 如何用
pandas從CSV/數(shù)據(jù)庫讀取并清洗數(shù)據(jù) - 如何用
openpyxl生成帶有格式和圖表的高顏值Excel報表 - 如何利用
win32com(結(jié)合PyPDF2)將Excel總結(jié)頁轉(zhuǎn)換為PDF - 如何用
smtplib自動發(fā)送帶附件的郵件
本文所有代碼基于Python 3.8+,請確保已安裝以下庫:
pip install pandas openpyxl PyPDF2 pywin32 # pywin32用于Windows COM操作 # 如需連接數(shù)據(jù)庫,還需安裝 sqlalchemy pymysql 等
注意:pywin32僅支持Windows系統(tǒng),如果你使用的是macOS或Linux,可以考慮使用libreoffice的命令行模式代替Excel的COM組件,或使用pdfkit將HTML轉(zhuǎn)換為PDF。本文將以Windows環(huán)境為例進行演示。
一、項目需求概述
假設(shè)我們是一家連鎖零售公司的數(shù)據(jù)分析師,需要每周一向管理層發(fā)送上一周的銷售報表。原始數(shù)據(jù)存儲在sales_data.csv文件中(也可從數(shù)據(jù)庫讀?。?,報表需包含以下內(nèi)容:
- 原始數(shù)據(jù)總覽:顯示上周所有銷售記錄
- 銷售匯總表:按區(qū)域、門店匯總銷售額、訂單量
- 趨勢圖表:每日銷售額趨勢圖(嵌入式圖表)
- 總結(jié)頁:包含核心指標(總銷售額、總訂單量、客單價)及關(guān)鍵結(jié)論
最終,我們需要將總結(jié)頁單獨轉(zhuǎn)換為PDF格式,與完整Excel報表一起作為郵件附件發(fā)送給指定收件人。
整個流程分為五個核心步驟:
- 步驟1:數(shù)據(jù)讀取 – 從CSV或數(shù)據(jù)庫加載原始數(shù)據(jù)
- 步驟2:數(shù)據(jù)清洗與分析 – 處理缺失值、計算統(tǒng)計指標
- 步驟3:Excel報表生成 – 使用
openpyxl創(chuàng)建多sheet工作簿,設(shè)置格式,插入圖表 - 步驟4:總結(jié)頁轉(zhuǎn)PDF – 利用
win32com調(diào)用Excel應(yīng)用程序,將指定sheet導(dǎo)出為PDF,并用PyPDF2進行額外處理(如加密) - 步驟5:郵件發(fā)送 – 通過SMTP發(fā)送帶附件的郵件
二、數(shù)據(jù)讀?。簭脑搭^獲取原始數(shù)據(jù)
原始數(shù)據(jù)可能以多種形式存在,最常見的是CSV文件和關(guān)系型數(shù)據(jù)庫。我們編寫一個通用函數(shù),根據(jù)配置選擇讀取方式。
2.1 從CSV讀取
假設(shè)sales_data.csv包含以下字段:date(日期)、region(區(qū)域)、store(門店)、product(產(chǎn)品)、quantity(銷量)、unit_price(單價)、total_amount(總金額)。
import pandas as pd
def read_csv_data(file_path):
df = pd.read_csv(file_path, parse_dates=['date'])
print(f"從CSV讀取數(shù)據(jù),共 {len(df)} 行")
return df
2.2 從數(shù)據(jù)庫讀?。ㄒ許QLite為例)
如果數(shù)據(jù)存儲在數(shù)據(jù)庫中,我們可以使用sqlalchemy建立連接。以下示例連接SQLite數(shù)據(jù)庫sales.db,讀取sales表上周的數(shù)據(jù)。
from sqlalchemy import create_engine
def read_db_data(db_url, table_name, start_date, end_date):
engine = create_engine(db_url)
query = f"""
SELECT * FROM {table_name}
WHERE date BETWEEN '{start_date}' AND '{end_date}'
"""
df = pd.read_sql(query, engine, parse_dates=['date'])
print(f"從數(shù)據(jù)庫讀取數(shù)據(jù),共 {len(df)} 行")
return df
為簡化示例,后續(xù)我們將以CSV文件為例。在實際項目中,你可以根據(jù)需求靈活選擇數(shù)據(jù)源。
三、數(shù)據(jù)清洗與分析
原始數(shù)據(jù)往往包含缺失值、異常值或格式不一致的問題,需要進行清洗。同時,我們需要計算報表所需的匯總指標。
3.1 數(shù)據(jù)清洗
常見的清洗操作包括:
- 刪除重復(fù)記錄
- 處理缺失值(如填充0或刪除)
- 確保數(shù)據(jù)類型正確(如將
total_amount轉(zhuǎn)為浮點數(shù)) - 過濾異常數(shù)據(jù)(如銷量為負的記錄)
def clean_data(df):
# 刪除重復(fù)行
df = df.drop_duplicates()
# 處理缺失值:這里假設(shè)total_amount不能為空,若為空則填充為0
df['total_amount'] = df['total_amount'].fillna(0)
# 確保數(shù)值列類型正確
df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce').fillna(0)
df['unit_price'] = pd.to_numeric(df['unit_price'], errors='coerce').fillna(0)
# 過濾:銷量和單價不能為負數(shù)
df = df[(df['quantity'] >= 0) & (df['unit_price'] >= 0)]
# 重新計算total_amount(防止原始數(shù)據(jù)有誤)
df['total_amount'] = df['quantity'] * df['unit_price']
return df
3.2 分析匯總
我們需要生成按區(qū)域和門店的銷售匯總表,以及每日銷售趨勢。
def analyze_data(df):
# 按區(qū)域和門店匯總
summary_by_store = df.groupby(['region', 'store']).agg(
total_sales=('total_amount', 'sum'),
order_count=('quantity', 'count'), # 假設(shè)每條記錄為一筆訂單
avg_order_value=('total_amount', 'mean')
).reset_index()
# 按日期匯總每日銷售額
daily_sales = df.groupby(df['date'].dt.date)['total_amount'].sum().reset_index()
daily_sales.columns = ['date', 'daily_total']
# 計算全局指標
total_sales = df['total_amount'].sum()
total_orders = len(df)
avg_customer_value = total_sales / total_orders if total_orders > 0 else 0
metrics = {
'total_sales': total_sales,
'total_orders': total_orders,
'avg_customer_value': avg_customer_value
}
return summary_by_store, daily_sales, metrics
四、生成精美Excel報表(使用openpyxl)
pandas自帶的to_excel功能只能輸出簡單數(shù)據(jù),無法滿足格式要求。我們將使用openpyxl引擎,手動創(chuàng)建工作簿、設(shè)置樣式、插入圖表。
4.1 創(chuàng)建工作簿并寫入數(shù)據(jù)
我們計劃生成一個包含三個sheet的Excel文件:
- 原始數(shù)據(jù):
Raw Data - 匯總表:
Summary by Store - 趨勢圖:
Daily Trend
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment, NamedStyle
from openpyxl.chart import LineChart, Reference
from openpyxl.utils.dataframe import dataframe_to_rows
import pandas as pd
def create_excel_report(df_raw, summary_df, daily_df, metrics, output_path):
wb = Workbook()
# 1. 原始數(shù)據(jù) sheet
ws_raw = wb.active
ws_raw.title = "Raw Data"
# 將DataFrame逐行寫入
for r in dataframe_to_rows(df_raw, index=False, header=True):
ws_raw.append(r)
# 2. 匯總表 sheet
ws_summary = wb.create_sheet("Summary by Store")
for r in dataframe_to_rows(summary_df, index=False, header=True):
ws_summary.append(r)
# 3. 每日趨勢 sheet
ws_trend = wb.create_sheet("Daily Trend")
for r in dataframe_to_rows(daily_df, index=False, header=True):
ws_trend.append(r)
# 保存臨時文件,后續(xù)還要進行格式化和圖表插入
wb.save(output_path)
return wb # 返回工作簿對象以便繼續(xù)操作
4.2 設(shè)置單元格格式
為了使報表專業(yè)易讀,我們需要對標題行加粗、填充背景色,設(shè)置數(shù)字格式,調(diào)整列寬等。
def format_excel(wb):
# 定義常用樣式
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill(start_color="4F81BD", end_color="4F81BD", fill_type="solid")
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
currency_style = NamedStyle(name="currency", number_format='"¥"#,##0.00')
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
# 設(shè)置標題行樣式(第一行)
for cell in ws[1]:
cell.font = header_font
cell.fill = header_fill
cell.border = thin_border
cell.alignment = Alignment(horizontal='center')
# 調(diào)整列寬
for col in ws.columns:
max_length = 0
col_letter = col[0].column_letter
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = min(max_length + 2, 50)
ws.column_dimensions[col_letter].width = adjusted_width
# 為包含金額的列應(yīng)用貨幣格式(假設(shè)列名包含'sales'或'amount')
for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
for cell in row:
cell.border = thin_border
if cell.column_letter in ['C','D']: # 根據(jù)實際列調(diào)整
cell.number_format = currency_style.number_format
return wb
4.3 插入圖表
在Daily Trend sheet中插入折線圖,展示每日銷售額變化。
from openpyxl.chart import LineChart, Reference
def add_chart(wb):
ws = wb["Daily Trend"]
# 數(shù)據(jù)范圍:假設(shè)日期在A列,銷售額在B列,從第2行開始到最后
data = Reference(ws, min_col=2, min_row=1, max_row=ws.max_row, max_col=2)
dates = Reference(ws, min_col=1, min_row=2, max_row=ws.max_row)
chart = LineChart()
chart.add_data(data, titles_from_data=True)
chart.set_categories(dates)
chart.title = "每日銷售額趨勢"
chart.x_axis.title = "日期"
chart.y_axis.title = "銷售額"
chart.style = 12 # 內(nèi)置樣式
# 將圖表放在E2單元格附近
ws.add_chart(chart, "E2")
return wb
4.4 創(chuàng)建總結(jié)頁
總結(jié)頁通常放在第一個sheet,包含關(guān)鍵指標和文字結(jié)論。我們可以單獨創(chuàng)建一個sheet,命名為“Summary”。
def create_summary_sheet(wb, metrics):
ws = wb.create_sheet("Summary", 0) # 插入到第一個位置
# 寫入標題
ws['A1'] = "銷售周報總結(jié)"
ws['A1'].font = Font(size=16, bold=True)
ws.merge_cells('A1:C1')
# 寫入核心指標
row = 3
ws[f'A{row}'] = "總銷售額:"
ws[f'B{row}'] = metrics['total_sales']
ws[f'B{row}'].number_format = '"¥"#,##0.00'
row += 1
ws[f'A{row}'] = "總訂單數(shù):"
ws[f'B{row}'] = metrics['total_orders']
row += 1
ws[f'A{row}'] = "客單價:"
ws[f'B{row}'] = metrics['avg_customer_value']
ws[f'B{row}'].number_format = '"¥"#,##0.00'
# 添加結(jié)論性文字
row += 2
ws[f'A{row}'] = "結(jié)論:"
row += 1
conclusion = f"上周總銷售額為¥{metrics['total_sales']:,.0f},共完成{metrics['total_orders']}筆訂單,客單價為¥{metrics['avg_customer_value']:.2f}。"
ws[f'A{row}'] = conclusion
ws.merge_cells(f'A{row}:C{row}')
# 調(diào)整列寬
ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 20
ws.column_dimensions['C'].width = 15
return wb
五、將總結(jié)頁轉(zhuǎn)換為PDF
這是本項目的關(guān)鍵難點。openpyxl無法直接導(dǎo)出PDF,而PyPDF2只能操作已有的PDF文件。在Windows企業(yè)環(huán)境中,最可靠的方式是借助Excel應(yīng)用程序本身(通過win32com)將指定sheet另存為PDF。然后,我們可以使用PyPDF2對生成的PDF進行額外處理,例如添加密碼保護或水印。
5.1 使用win32com導(dǎo)出PDF
import win32com.client
import os
def export_sheet_to_pdf(excel_path, sheet_name, pdf_path):
"""
使用Excel COM對象將指定sheet導(dǎo)出為PDF
"""
excel = win32com.client.Dispatch("Excel.Application")
excel.Visible = False # 后臺運行
try:
wb = excel.Workbooks.Open(os.path.abspath(excel_path))
ws = wb.Sheets(sheet_name)
# 導(dǎo)出為PDF
ws.ExportAsFixedFormat(0, pdf_path) # 0代表xlTypePDF
print(f"已導(dǎo)出 {sheet_name} 到 {pdf_path}")
except Exception as e:
print(f"導(dǎo)出PDF失敗: {e}")
raise
finally:
wb.Close(SaveChanges=False)
excel.Quit()
5.2 使用PyPDF2添加加密(可選)
假設(shè)我們希望PDF文件需要密碼才能打開,可以使用PyPDF2對生成的PDF進行加密。
import PyPDF2
def encrypt_pdf(input_pdf, output_pdf, password):
with open(input_pdf, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
pdf_writer = PyPDF2.PdfWriter()
for page_num in range(len(pdf_reader.pages)):
pdf_writer.add_page(pdf_reader.pages[page_num])
pdf_writer.encrypt(password)
with open(output_pdf, 'wb') as out_file:
pdf_writer.write(out_file)
print(f"PDF已加密,保存至 {output_pdf}")
六、發(fā)送郵件附件
最后一步,將生成的Excel文件和PDF文件作為附件,通過郵件發(fā)送給指定收件人。我們使用Python內(nèi)置的smtplib和email庫。
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
def send_email(sender, password, receiver, subject, body, attachments):
# 創(chuàng)建郵件對象
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = subject
# 添加正文
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 添加附件
for file_path in attachments:
with open(file_path, 'rb') as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename= {os.path.basename(file_path)}'
)
msg.attach(part)
# 發(fā)送郵件(以QQ郵箱為例)
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.send_message(msg)
server.quit()
print("郵件發(fā)送成功")
except Exception as e:
print(f"郵件發(fā)送失敗: {e}")
七、整合全流程:一鍵生成報表
現(xiàn)在,我們將所有步驟封裝成一個函數(shù)generate_report,實現(xiàn)從數(shù)據(jù)到郵件的一鍵自動化。
def generate_report(csv_path, excel_output, pdf_output, email_config):
# 1. 讀取數(shù)據(jù)
df_raw = read_csv_data(csv_path)
# 2. 清洗與分析
df_clean = clean_data(df_raw)
summary_df, daily_df, metrics = analyze_data(df_clean)
# 3. 生成Excel(先保存基本數(shù)據(jù))
wb = create_excel_report(df_clean, summary_df, daily_df, metrics, excel_output)
wb = format_excel(wb)
wb = add_chart(wb)
wb = create_summary_sheet(wb, metrics)
wb.save(excel_output)
print(f"Excel報表已生成: {excel_output}")
# 4. 將Summary sheet導(dǎo)出為PDF
export_sheet_to_pdf(excel_output, "Summary", pdf_output)
# 可選:用PyPDF2加密PDF
# pdf_encrypted = pdf_output.replace('.pdf', '_encrypted.pdf')
# encrypt_pdf(pdf_output, pdf_encrypted, 'weekly123')
# 5. 發(fā)送郵件
send_email(
sender=email_config['sender'],
password=email_config['password'],
receiver=email_config['receiver'],
subject=email_config['subject'],
body=email_config['body'],
attachments=[excel_output, pdf_output] # 同時發(fā)送Excel和PDF
)
print("報表生成及發(fā)送流程全部完成!")
使用示例:
if __name__ == "__main__":
csv_file = "data/sales_data.csv"
excel_file = "output/sales_report.xlsx"
pdf_file = "output/summary.pdf"
email_cfg = {
'sender': 'your_email@qq.com',
'password': 'your_authorization_code', # QQ郵箱授權(quán)碼
'receiver': 'boss@company.com',
'subject': '上周銷售報表',
'body': '您好,附件是上周銷售報表,請查收。\n\n數(shù)據(jù)分析團隊'
}
generate_report(csv_file, excel_file, pdf_file, email_cfg)
八、總結(jié)與擴展
通過本實戰(zhàn)項目,我們成功構(gòu)建了一個自動化報表生成器,涵蓋了從數(shù)據(jù)讀取、清洗分析、Excel格式化、PDF導(dǎo)出到郵件發(fā)送的完整流程。這個工具可以每周定時運行(配合Windows任務(wù)計劃程序或Linux cron),徹底解放人力。
以上就是Python從零構(gòu)建一個自動化報表生成器的詳細內(nèi)容,更多關(guān)于Python自動化報表生成器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
這篇文章主要為大家介紹了python中selenium模塊的安裝和配置環(huán)境變量教程、提取數(shù)據(jù)操作、無頭模式,有需要的朋友可以借鑒參考下,希望能夠?qū)Υ蠹矣兴鶐椭?/div> 2022-10-10
Python利用pyecharts實現(xiàn)數(shù)據(jù)可視化的示例代碼
Pyecharts是一個用于生成 Echarts 圖表的 Python 庫,Echarts 是一個由百度開源的數(shù)據(jù)可視化工具,它提供的圖表種類豐富,交互性強,兼容性好,非常適合用于數(shù)據(jù)分析結(jié)果的展示,本文將給大家介紹Python利用pyecharts實現(xiàn)數(shù)據(jù)可視化,需要的朋友可以參考下2024-09-09最新評論

