97·模块十八:综合项目实战进阶

项目实战:自动化办公脚本(文件处理、邮件、报告)

projectautomationscripting

第 97 课 - 项目实战:自动化办公脚本(文件处理、邮件、报告)

所属模块:模块十八:综合项目实战 难度:中级 标签:项目, 自动化, 脚本 上一课:项目实战:完成一个端到端的数据分析项目 下一课:项目实战:开发一个桌面 GUI 应用程序

学习目标

完成本课学习后,你将能够:

  1. 整合 Python 的文件处理、数据处理、报告生成和邮件发送能力,构建一个完整的自动化工作流。
  2. 使用 osshutil 模块进行文件与目录的自动化操作(如移动、重命名、清理)。
  3. 使用 csvopenpyxl 等库读取数据文件并进行简单的汇总计算。
  4. 使用 xlsxwriteropenpyxl 库创建结构清晰的 Excel 报告。
  5. 使用 smtplibemail 库编写脚本,自动发送带有附件的电子邮件。

核心概念

自动化办公脚本的核心思想是将重复性、规则性强的手工操作,交给程序自动执行。在本课中,我们将围绕一个模拟的"周报自动化"场景,将以下三个关键环节串联起来:

  1. 文件处理 (File Handling):脚本的起点。我们需要从指定的文件夹(如 data/raw)中读取原始数据文件(如 .csv 销售记录),并在处理完毕后,将文件移动到存档目录(如 data/archive),同时清理临时文件。
  2. 数据处理与报告生成 (Data Processing & Reporting):脚本的核心。读取原始数据后,使用 Python 进行计算(如本周销售总额、各产品销量排名),然后将结果和原始数据,以美观的格式(如带图表、表头)写入一个新的 Excel 工作簿(report.xlsx)作为报告。
  3. 邮件发送 (Email Sending):脚本的终点。将生成的报告作为附件,通过 SMTP 协议自动发送给指定的收件人(如你的经理或团队),并附上一封简明的邮件正文。

这三步环环相扣,形成一个无人值守的自动化流水线。

代码示例

下面是一个整合了上述所有概念的脚本示例。它假设你在 sales_data.csv 文件中有如下的销售记录。

第一步:准备模拟数据文件 (sales_data.csv)

Date,Product,Quantity,UnitPrice
2023-10-01,Laptop,5,999.9
2023-10-01,Mouse,10,25.5
2023-10-02,Laptop,3,999.9
2023-10-02,Keyboard,8,75.0
2023-10-03,Mouse,12,25.5

第二步:自动化脚本 (auto_weekly_report.py)

import os
import shutil
import csv
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
import xlsxwriter # 需要安装: pip install xlsxwriter

# ==================== 1. 配置部分 ====================
RAW_DATA_DIR = 'data/raw'          # 原始数据存放目录
ARCHIVE_DIR = 'data/archive'        # 处理后的文件归档目录
REPORT_FILENAME = 'weekly_report.xlsx'  # 生成的报告文件名

# 邮件配置(请替换为你的真实信息)
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587
SENDER_EMAIL = '[email protected]'
SENDER_PASSWORD = 'your_password'  # 注意:此处为示例,实际应使用应用专用密码或环境变量
RECEIVER_EMAIL = ['[email protected]']  # 收件人列表
SUBJECT = f'自动化周报 - {datetime.date.today().strftime("%Y-%m-%d")}'

# ==================== 2. 核心功能函数 ====================

def process_sales_data(filepath):
    """读取并汇总CSV销售数据"""
    total_revenue = 0
    product_summary = {}
    
    try:
        with open(filepath, 'r', newline='', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                quantity = int(row['Quantity'])
                unit_price = float(row['UnitPrice'])
                revenue = quantity * unit_price
                total_revenue += revenue
                
                product = row['Product']
                if product in product_summary:
                    product_summary[product] += quantity
                else:
                    product_summary[product] = quantity
                    
        return total_revenue, product_summary
    except FileNotFoundError:
        print(f"错误:找不到数据文件 {filepath}")
        return None, None

def create_excel_report(total_revenue, product_summary, output_path):
    """创建带图表的Excel报告"""
    workbook = xlsxwriter.Workbook(output_path)
    worksheet = workbook.add_worksheet('销售汇总')
    
    # 添加格式
    bold = workbook.add_format({'bold': True})
    money_format = workbook.add_format({'num_format': '$#,##0.00'})
    
    # 写入标题
    worksheet.write('A1', '本周销售数据汇总', bold)
    worksheet.write('A3', '总销售额:', bold)
    worksheet.write('B3', total_revenue, money_format)
    
    # 写入产品明细表
    worksheet.write('A5', '产品', bold)
    worksheet.write('B5', '总销量', bold)
    row = 5
    for product, qty in product_summary.items():
        worksheet.write(row, 0, product)
        worksheet.write(row, 1, qty)
        row += 1
    
    # 创建一个柱状图
    chart = workbook.add_chart({'type': 'column'})
    chart.add_series({
        'name': '销量',
        'categories': f'=销售汇总!$A$6:$A${row-1}',  # 动态范围
        'values':     f'=销售汇总!$B$6:$B${row-1}',
    })
    chart.set_title({'name': '产品销量对比'})
    chart.set_y_axis({'name': '销量(件)'})
    worksheet.insert_chart('D2', chart)
    
    workbook.close()
    print(f"报告已生成: {output_path}")

def send_email_with_attachment(subject, body, attachment_path):
    """发送带附件的电子邮件"""
    # 创建邮件对象
    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = ', '.join(RECEIVER_EMAIL)
    msg['Subject'] = subject
    
    # 添加邮件正文
    msg.attach(MIMEText(body, 'plain'))
    
    # 添加附件
    with open(attachment_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(attachment_path)}')
    msg.attach(part)
    
    # 发送邮件
    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.starttls()  # 启用TLS加密
            server.login(SENDER_EMAIL, SENDER_PASSWORD)
            server.sendmail(SENDER_EMAIL, RECEIVER_EMAIL, msg.as_string())
        print("邮件发送成功!")
    except Exception as e:
        print(f"邮件发送失败: {e}")

def archive_file(src_path, dest_dir):
    """将处理完的文件移动到归档目录"""
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)
    filename = os.path.basename(src_path)
    dest_path = os.path.join(dest_dir, filename)
    shutil.move(src_path, dest_path)
    print(f"文件已归档至: {dest_path}")

# ==================== 3. 主程序:串联整个自动化流程 ====================

if __name__ == "__main__":
    # 假设我们处理最新的数据文件(这里简化为固定文件名)
    source_data_file = os.path.join(RAW_DATA_DIR, 'sales_data.csv')
    
    # 步骤1: 数据处理
    print("开始处理销售数据...")
    total, summary = process_sales_data(source_data_file)
    
    if total is not None:
        # 步骤2: 生成报告
        report_path = os.path.join(os.getcwd(), REPORT_FILENAME)
        create_excel_report(total, summary, report_path)
        
        # 步骤3: 发送邮件
        email_body = "你好,\n\n请查收本周的销售数据汇总报告。\n\n此邮件由Python自动化脚本发送。"
        send_email_with_attachment(SUBJECT, email_body, report_path)
        
        # 步骤4: 归档原始数据文件
        archive_file(source_data_file, ARCHIVE_DIR)
        
        # 步骤5(可选):清理生成的报告(如果不需要在本地保留)
        # os.remove(report_path)
        print("自动化流程全部完成!")
    else:
        print("数据处理失败,流程终止。")

实践练习

练习 1(基础):文件批量重命名 编写一个函数 batch_rename(directory, old_ext, new_ext),它接受一个目录路径、旧文件扩展名和新文件扩展名作为参数。函数会将该目录下所有指定扩展名的文件,重命名为新的扩展名。

  • 要求:使用 os 模块。
  • 示例:将目录 test_folder 下所有 .txt 文件重命名为 .md 文件。
  • 预期输出:在控制台打印每个重命名操作,如 Renamed: file1.txt -> file1.md

练习 2(进阶):合并多个CSV文件 假设一个目录下有多个结构相同(列名一致)的 sales_data_week1.csv, sales_data_week2.csv 等文件。编写脚本,将它们合并成一个大的 all_sales_data.csv 文件。

  • 要求:使用 csv 模块读写文件。
  • 提示:可以先读取第一个文件的表头,然后循环读取所有文件的数据行。

练习 3(综合挑战):自定义报表邮件 修改本课示例脚本,增加以下功能:

  1. 在邮件正文中,用文字简要列出"总销售额"和"销量最高的产品"。
  2. 在报告 Excel 中,除了柱状图,再增加一个显示销售额占比的饼图。
  3. (挑战) 脚本能自动检测 data/raw 目录下最新的 .csv 文件,而不是写死文件名。

常见错误

  1. 文件路径错误:使用相对路径时,脚本的工作目录(cwd)可能与你预期的不同。建议使用 os.path.join__file__ 变量来构建绝对路径。
    # 错误的写法(可能因运行脚本的位置不同而找不到文件)
    file_path = 'data/raw/sales.csv'
    # 更健壮的写法
    script_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(script_dir, 'data', 'raw', 'sales.csv')
    
  2. 邮件发送失败
    • 认证错误:很多邮箱(如QQ、163、Gmail)需要使用"应用专用密码"而非账户密码。请检查你的邮箱SMTP设置和密码。
    • 连接错误:确保SMTP服务器地址和端口正确(例如Gmail用smtp.gmail.com,端口587)。
    • 防火墙/网络:确保你的网络环境允许连接外部的SMTP服务器。
  3. 编码问题:读取或写入包含中文等非ASCII字符的文件时,务必指定正确的编码(如 encoding='utf-8')。
  4. 资源未释放:使用 with open(...) 语句可以确保文件被正确关闭,避免资源泄露。
  5. 第三方库未安装:示例中的 xlsxwriter 需要提前安装。使用 pip install xlsxwriter 命令。

小结

本课通过一个"周报自动化"的完整项目,演示了如何将 Python 的文件操作、数据处理、报告生成和邮件发送能力融合,构建一个实用的办公自动化脚本。

关键要点回顾

  • 模块化设计:将复杂流程拆分为独立的函数(如 process_sales_data, create_excel_report),使代码更清晰、易维护、可复用。
  • 文件处理三部曲:读取 -> 处理 -> 归档/清理,是自动化脚本的典型模式。
  • 综合运用库csv 处理表格数据,xlsxwriter/openpyxl 生成专业报告,smtplib + email 实现邮件功能。
  • 健壮性考虑:在代码中加入 try...except 块处理文件找不到、网络错误等异常,并妥善处理文件路径和编码问题。
  • 安全提醒:切勿将密码等敏感信息硬编码在脚本中,应使用环境变量或配置文件来管理。

掌握了这个自动化流程框架后,你可以轻松地将其扩展到其他场景,如自动化数据备份、定期清理日志文件、监控系统状态并发送告警邮件等。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「项目实战:开发一个桌面 GUI 应用程序」 以巩固所学知识。