【办公自动化】使用Python来自动化处理Excel表格有哪些方法?

在日常办公中,Excel是最常用的数据处理工具之一。通过Python自动化Excel操作,可以大幅提高工作效率,减少重复劳动,降低人为错误。本文将介绍几种常用的Python操作Excel的方法,并提供实用的代码示例和应用场景。

组合xlrd、xlwt、xlutils实现excel读写操作

这三个库是早期Python操作Excel的经典组合,各司其职:xlrd负责读取,xlwt负责写入,xlutils作为两者的桥梁。虽然现在有了更强大的库,但在一些特定场景下,这个组合仍然有其价值。

安装这些库

python 复制代码
pip install xlrd xlwt xlutils

读取Excel文件

python 复制代码
import xlrd

def read_excel_file(file_path):
    """读取Excel文件并打印内容"""
    # 打开工作簿
    workbook = xlrd.open_workbook(file_path)
    
    # 获取所有工作表名称
    sheet_names = workbook.sheet_names()
    print(f"工作表列表: {sheet_names}")
    
    # 遍历每个工作表
    for sheet_name in sheet_names:
        sheet = workbook.sheet_by_name(sheet_name)
        print(f"\n工作表: {sheet_name}, 行数: {sheet.nrows}, 列数: {sheet.ncols}")
        
        # 打印表头
        if sheet.nrows > 0:
            header = [sheet.cell_value(0, col) for col in range(sheet.ncols)]
            print(f"表头: {header}")
        
        # 打印数据(最多显示5行)
        for row in range(1, min(6, sheet.nrows)):
            row_data = [sheet.cell_value(row, col) for col in range(sheet.ncols)]
            print(f"第{row}行: {row_data}")

# 使用示例
read_excel_file("员工信息.xls")

写入Excel文件

python 复制代码
import xlwt

def create_excel_file(file_path):
    """创建新的Excel文件"""
    # 创建工作簿
    workbook = xlwt.Workbook(encoding='utf-8')
    
    # 添加工作表
    sheet = workbook.add_sheet('员工信息')
    
    # 定义样式
    header_style = xlwt.easyxf('font: bold on; align: horiz center')
    date_style = xlwt.easyxf(num_format_str='yyyy-mm-dd')
    
    # 写入表头
    headers = ['ID', '姓名', '部门', '入职日期', '薪资']
    for col, header in enumerate(headers):
        sheet.write(0, col, header, header_style)
    
    # 准备数据
    data = [
        [1001, '张三', '技术部', '2020-01-15', 12000],
        [1002, '李四', '市场部', '2019-05-23', 15000],
        [1003, '王五', '财务部', '2021-03-08', 13500],
        [1004, '赵六', '人事部', '2018-11-12', 14000],
    ]
    
    # 写入数据
    for row, row_data in enumerate(data, 1):
        for col, cell_value in enumerate(row_data):
            # 对日期使用特殊格式
            if col == 3:  # 入职日期列
                import datetime
                date_parts = cell_value.split('-')
                date_obj = datetime.datetime(int(date_parts[0]), int(date_parts[1]), int(date_parts[2]))
                sheet.write(row, col, date_obj, date_style)
            else:
                sheet.write(row, col, cell_value)
    
    # 保存文件
    workbook.save(file_path)
    print(f"Excel文件已创建: {file_path}")

# 使用示例
create_excel_file("新员工信息.xls")

修改现有Excel文件

python 复制代码
import xlrd
import xlwt
from xlutils.copy import copy

def update_excel_file(file_path, employee_id, new_salary):
    """更新指定员工的薪资信息"""
    # 打开原工作簿(只读模式)
    rb = xlrd.open_workbook(file_path, formatting_info=True)
    sheet = rb.sheet_by_index(0)
    
    # 创建一个可写的副本
    wb = copy(rb)
    w_sheet = wb.get_sheet(0)
    
    # 查找员工ID并更新薪资
    found = False
    for row in range(1, sheet.nrows):
        if int(sheet.cell_value(row, 0)) == employee_id:  # 假设ID在第一列
            w_sheet.write(row, 4, new_salary)  # 假设薪资在第五列
            found = True
            break
    
    if found:
        # 保存修改后的文件
        wb.save(file_path)
        print(f"已更新员工ID {employee_id} 的薪资为 {new_salary}")
    else:
        print(f"未找到员工ID: {employee_id}")

# 使用示例
update_excel_file("员工信息.xls", 1002, 16000)

使用openpyxl实现excel的读写修改

openpyxl是目前最流行的Python Excel处理库之一,功能全面,API友好,特别适合处理较新的Excel格式(.xlsx)。

安装openpyxl

python 复制代码
pip install openpyxl

读取Excel文件

python 复制代码
from openpyxl import load_workbook

def read_excel_with_openpyxl(file_path):
    """使用openpyxl读取Excel文件"""
    # 加载工作簿
    wb = load_workbook(file_path, read_only=True)
    
    # 获取所有工作表名称
    sheet_names = wb.sheetnames
    print(f"工作表列表: {sheet_names}")
    
    # 遍历每个工作表
    for sheet_name in sheet_names:
        sheet = wb[sheet_name]
        print(f"\n工作表: {sheet_name}")
        
        # 获取表格尺寸
        if not sheet.max_row:  # 对于read_only模式,需要遍历才能获取尺寸
            print("工作表为空或使用read_only模式无法直接获取尺寸")
            continue
        
        # 打印表头
        header = [cell.value for cell in next(sheet.iter_rows())]
        print(f"表头: {header}")
        
        # 打印数据(最多5行)
        row_count = 0
        for row in sheet.iter_rows(min_row=2):  # 从第二行开始
            if row_count >= 5:
                break
            row_data = [cell.value for cell in row]
            print(f"行 {row_count + 2}: {row_data}")
            row_count += 1
    
    # 关闭工作簿
    wb.close()

# 使用示例
read_excel_with_openpyxl("员工信息.xlsx")

创建新的Excel文件

python 复制代码
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill
from openpyxl.utils import get_column_letter
import datetime

def create_excel_with_openpyxl(file_path):
    """使用openpyxl创建格式化的Excel文件"""
    # 创建工作簿
    wb = Workbook()
    sheet = wb.active
    sheet.title = "销售数据"
    
    # 定义样式
    header_font = Font(name='Arial', size=12, bold=True, color="FFFFFF")
    header_fill = PatternFill("solid", fgColor="4F81BD")
    centered = Alignment(horizontal="center")
    
    # 写入表头
    headers = ['产品ID', '产品名称', '类别', '单价', '销售日期', '销售量', '销售额']
    for col_num, header in enumerate(headers, 1):
        cell = sheet.cell(row=1, column=col_num)
        cell.value = header
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = centered
    
    # 准备数据
    data = [
        [101, '笔记本电脑', '电子产品', 5999, datetime.date(2023, 1, 15), 10, '=D2*F2'],
        [102, '办公椅', '办公家具', 899, datetime.date(2023, 1, 16), 20, '=D3*F3'],
        [103, '打印机', '办公设备', 1299, datetime.date(2023, 1, 18), 5, '=D4*F4'],
        [104, '显示器', '电子产品', 1499, datetime.date(2023, 1, 20), 15, '=D5*F5'],
        [105, '文件柜', '办公家具', 699, datetime.date(2023, 1, 22), 8, '=D6*F6'],
    ]
    
    # 写入数据
    for row_num, row_data in enumerate(data, 2):
        for col_num, cell_value in enumerate(row_data, 1):
            cell = sheet.cell(row=row_num, column=col_num)
            cell.value = cell_value
            if col_num == 5:  # 日期列使用日期格式
                cell.number_format = 'yyyy-mm-dd'
            elif col_num == 7:  # 销售额列使用公式和货币格式
                cell.number_format = '¥#,##0.00'
    
    # 添加合计行
    total_row = len(data) + 2
    sheet.cell(row=total_row, column=1).value = "合计"
    sheet.cell(row=total_row, column=1).font = Font(bold=True)
    
    # 销售量合计
    sheet.cell(row=total_row, column=6).value = f"=SUM(F2:F{total_row-1})"
    sheet.cell(row=total_row, column=6).font = Font(bold=True)
    
    # 销售额合计
    sheet.cell(row=total_row, column=7).value = f"=SUM(G2:G{total_row-1})"
    sheet.cell(row=total_row, column=7).font = Font(bold=True)
    sheet.cell(row=total_row, column=7).number_format = '¥#,##0.00'
    
    # 调整列宽
    for col in range(1, len(headers) + 1):
        sheet.column_dimensions[get_column_letter(col)].width = 15
    
    # 保存文件
    wb.save(file_path)
    print(f"Excel文件已创建: {file_path}")

# 使用示例
create_excel_with_openpyxl("销售数据.xlsx")

处理大型Excel文件

python 复制代码
from openpyxl import load_workbook
import time

def process_large_excel(file_path):
    """使用read_only模式处理大型Excel文件"""
    start_time = time.time()
    
    # 使用read_only模式加载工作簿
    wb = load_workbook(file_path, read_only=True)
    sheet = wb.active
    
    # 统计数据
    row_count = 0
    sum_value = 0
    
    # 假设第5列是数值,我们要计算其总和
    for row in sheet.iter_rows(min_row=2, values_only=True):
        row_count += 1
        if len(row) >= 5 and row[4] is not None:
            try:
                sum_value += float(row[4])
            except (ValueError, TypeError):
                pass
        
        # 每处理10000行打印一次进度
        if row_count % 10000 == 0:
            print(f"已处理 {row_count} 行...")
    
    # 关闭工作簿
    wb.close()
    
    end_time = time.time()
    print(f"处理完成,共 {row_count} 行数据")
    print(f"第5列数值总和: {sum_value}")
    print(f"处理时间: {end_time - start_time:.2f} 秒")

# 使用示例(对于大型文件)
# process_large_excel("大型数据集.xlsx")

使用xlwings模块操控excel文档

xlwings是一个强大的库,可以直接与Excel应用程序交互,实现自动化操作,甚至可以调用Excel的VBA函数。

安装xlwings

python 复制代码
pip install xlwings

基本操作

python 复制代码
import xlwings as xw

def automate_excel_with_xlwings():
    """使用xlwings自动化Excel操作"""
    # 启动Excel应用
    app = xw.App(visible=True)  # visible=True让Excel可见,便于观察操作过程
    
    try:
        # 创建新工作簿
        wb = app.books.add()
        sheet = wb.sheets[0]
        sheet.name = "销售报表"
        
        # 添加表头
        sheet.range("A1").value = "产品"
        sheet.range("B1").value = "一季度"
        sheet.range("C1").value = "二季度"
        sheet.range("D1").value = "三季度"
        sheet.range("E1").value = "四季度"
        sheet.range("F1").value = "年度总计"
        
        # 设置表头格式
        header_range = sheet.range("A1:F1")
        header_range.color = (0, 112, 192)  # 蓝色背景
        header_range.font.color = (255, 255, 255)  # 白色文字
        header_range.font.bold = True
        
        # 添加数据
        data = [
            ["产品A", 100, 120, 140, 130],
            ["产品B", 90, 100, 110, 120],
            ["产品C", 80, 85, 90, 95],
        ]
        
        # 写入数据
        sheet.range("A2").value = data
        
        # 添加公式计算年度总计
        for i in range(len(data)):
            row = i + 2  # 数据从第2行开始
            sheet.range(f"F{row}").formula = f"=SUM(B{row}:E{row})"
        
        # 添加合计行
        total_row = len(data) + 2
        sheet.range(f"A{total_row}").value = "合计"
        sheet.range(f"A{total_row}").font.bold = True
        
        # 添加合计公式
        for col in "BCDEF":
            sheet.range(f"{col}{total_row}").formula = f"=SUM({col}2:{col}{total_row-1})"
            sheet.range(f"{col}{total_row}").font.bold = True
        
        # 添加图表
        chart = sheet.charts.add()
        chart.set_source_data(sheet.range(f"A1:E{len(data)+1}"))
        chart.chart_type = "column_clustered"
        chart.name = "季度销售图表"
        chart.top = sheet.range(f"A{total_row+2}").top
        chart.left = sheet.range("A1").left
        
        # 调整列宽
        sheet.autofit()
        
        # 保存文件
        wb.save("xlwings_销售报表.xlsx")
        print("Excel文件已创建并保存")
        
    finally:
        # 关闭工作簿和应用
        wb.close()
        app.quit()

# 使用示例
# automate_excel_with_xlwings()

与Excel VBA结合使用

python 复制代码
import xlwings as xw

def run_excel_macro():
    """运行Excel中的VBA宏"""
    # 打开包含宏的工作簿
    wb = xw.Book("带宏的工作簿.xlsm")
    
    try:
        # 运行名为'ProcessData'的宏
        wb.macro("ProcessData")()
        print("宏已执行完成")
        
        # 读取宏处理后的结果
        sheet = wb.sheets["结果"]
        result = sheet.range("A1:C10").value
        print("处理结果:")
        for row in result:
            print(row)
        
    finally:
        # 保存并关闭工作簿
        wb.save()
        wb.close()

# 使用示例(需要有包含'ProcessData'宏的Excel文件)
# run_excel_macro()

使用Pandas轻松处理多个excel工作薄

Pandas是数据分析的利器,它提供了强大的数据结构和操作功能,特别适合处理表格数据。

安装Pandas

python 复制代码
pip install pandas

读取Excel文件

python 复制代码
import pandas as pd

def read_excel_with_pandas(file_path):
    """使用pandas读取Excel文件"""
    # 读取所有工作表
    xlsx = pd.ExcelFile(file_path)
    
    # 获取所有工作表名称
    sheet_names = xlsx.sheet_names
    print(f"工作表列表: {sheet_names}")
    
    # 遍历每个工作表
    for sheet_name in sheet_names:
        # 读取工作表到DataFrame
        df = pd.read_excel(xlsx, sheet_name)
        
        print(f"\n工作表: {sheet_name}, 形状: {df.shape}")
        
        # 显示前5行数据
        print("\n数据预览:")
        print(df.head())
        
        # 显示基本统计信息
        print("\n数值列统计信息:")
        print(df.describe())

# 使用示例
read_excel_with_pandas("销售数据.xlsx")

数据处理与分析

python 复制代码
import pandas as pd
import matplotlib.pyplot as plt

def analyze_sales_data(file_path):
    """使用pandas分析销售数据"""
    # 读取Excel文件
    df = pd.read_excel(file_path)
    
    # 显示基本信息
    print("数据基本信息:")
    print(df.info())
    
    # 按类别分组统计
    category_stats = df.groupby('类别').agg({
        '销售量': 'sum',
        '销售额': 'sum'
    }).sort_values('销售额', ascending=False)
    
    print("\n按类别统计:")
    print(category_stats)
    
    # 按月份分析销售趋势
    df['月份'] = pd.to_datetime(df['销售日期']).dt.month
    monthly_sales = df.groupby('月份').agg({
        '销售量': 'sum',
        '销售额': 'sum'
    })
    
    print("\n按月份统计:")
    print(monthly_sales)
    
    # 创建图表
    plt.figure(figsize=(12, 5))
    
    # 销售额柱状图
    plt.subplot(1, 2, 1)
    category_stats['销售额'].plot(kind='bar', color='skyblue')
    plt.title('各类别销售额')
    plt.ylabel('销售额')
    plt.xticks(rotation=45)
    
    # 月度销售趋势图
    plt.subplot(1, 2, 2)
    monthly_sales['销售额'].plot(marker='o', color='green')
    plt.title('月度销售趋势')
    plt.xlabel('月份')
    plt.ylabel('销售额')
    
    plt.tight_layout()
    plt.savefig('销售分析.png')
    plt.close()
    
    print("\n分析图表已保存为'销售分析.png'")
    
    # 返回处理后的数据
    return df, category_stats, monthly_sales

# 使用示例
# analyze_sales_data("销售数据.xlsx")

合并多个Excel文件

python 复制代码
import pandas as pd
import os

def merge_excel_files(directory, output_file):
    """合并目录下的所有Excel文件"""
    # 获取目录下所有Excel文件
    excel_files = [f for f in os.listdir(directory) 
                  if f.endswith('.xlsx') or f.endswith('.xls')]
    
    if not excel_files:
        print(f"目录 {directory} 中没有找到Excel文件")
        return
    
    print(f"找到 {len(excel_files)} 个Excel文件")
    
    # 创建一个空的DataFrame列表
    dfs = []
    
    # 读取每个Excel文件
    for file in excel_files:
        file_path = os.path.join(directory, file)
        print(f"处理文件: {file}")
        
        # 读取所有工作表
        xlsx = pd.ExcelFile(file_path)
        
        for sheet_name in xlsx.sheet_names:
            # 读取工作表
            df = pd.read_excel(xlsx, sheet_name)
            
            # 添加文件名和工作表名列
            df['源文件'] = file
            df['工作表'] = sheet_name
            
            # 添加到列表
            dfs.append(df)
    
    # 合并所有DataFrame
    if dfs:
        merged_df = pd.concat(dfs, ignore_index=True)
        
        # 保存合并后的数据
        merged_df.to_excel(output_file, index=False)
        print(f"已将 {len(dfs)} 个工作表合并到 {output_file}")
        print(f"合并后的数据形状: {merged_df.shape}")
    else:
        print("没有找到有效的数据表")

# 使用示例
# merge_excel_files("excel_files", "合并数据.xlsx")

实际应用场景

场景一:销售数据自动化报表

python 复制代码
import pandas as pd
import matplotlib.pyplot as plt
from openpyxl import Workbook
from openpyxl.chart import BarChart, Reference, LineChart
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils import get_column_letter
import datetime

def generate_sales_report(sales_data_file, output_file):
    """生成销售数据分析报表"""
    # 读取销售数据
    df = pd.read_excel(sales_data_file)
    
    # 数据清洗和准备
    df['销售日期'] = pd.to_datetime(df['销售日期'])
    df['月份'] = df['销售日期'].dt.month
    df['季度'] = df['销售日期'].dt.quarter
    
    # 按产品和月份分组统计
    product_monthly = df.pivot_table(
        index='产品名称',
        columns='月份',
        values='销售额',
        aggfunc='sum',
        fill_value=0
    )
    
    # 按类别和季度分组统计
    category_quarterly = df.pivot_table(
        index='类别',
        columns='季度',
        values=['销售量', '销售额'],
        aggfunc='sum',
        fill_value=0
    )
    
    # 计算总计和环比
    product_monthly['总计'] = product_monthly.sum(axis=1)
    product_monthly = product_monthly.sort_values('总计', ascending=False)
    
    # 创建Excel工作簿
    wb = Workbook()
    
    # 创建产品月度销售工作表
    ws1 = wb.active
    ws1.title = "产品月度销售"
    
    # 写入表头
    headers = ['产品名称'] + [f"{i}月" for i in sorted(product_monthly.columns[:-1])] + ['总计']
    for col_num, header in enumerate(headers, 1):
        cell = ws1.cell(row=1, column=col_num)
        cell.value = header
        cell.font = Font(bold=True)
        cell.fill = PatternFill("solid", fgColor="4F81BD")
        cell.alignment = Alignment(horizontal="center")
    
    # 写入数据
    for row_num, (index, data) in enumerate(product_monthly.iterrows(), 2):
        ws1.cell(row=row_num, column=1).value = index  # 产品名称
        
        for col_num, value in enumerate(data.values, 2):
            cell = ws1.cell(row=row_num, column=col_num)
            cell.value = value
            cell.number_format = '#,##0.00'
    
    # 添加合计行
    total_row = len(product_monthly) + 2
    ws1.cell(row=total_row, column=1).value = "总计"
    ws1.cell(row=total_row, column=1).font = Font(bold=True)
    
    for col in range(2, len(headers) + 1):
        col_letter = get_column_letter(col)
        ws1.cell(row=total_row, column=col).value = f"=SUM({col_letter}2:{col_letter}{total_row-1})"
        ws1.cell(row=total_row, column=col).font = Font(bold=True)
        ws1.cell(row=total_row, column=col).number_format = '#,##0.00'
    
    # 创建图表
    chart = BarChart()
    chart.title = "产品销售额对比"
    chart.x_axis.title = "产品"
    chart.y_axis.title = "销售额"
    
    # 设置图表数据范围
    data = Reference(ws1, min_col=2, min_row=1, max_row=min(11, total_row-1), max_col=len(headers)-1)
    cats = Reference(ws1, min_col=1, min_row=2, max_row=min(11, total_row-1))
    
    chart.add_data(data, titles_from_data=True)
    chart.set_categories(cats)
    
    # 添加图表到工作表
    ws1.add_chart(chart, "A" + str(total_row + 2))
    
    # 创建类别季度销售工作表
    ws2 = wb.create_sheet(title="类别季度分析")
    
    # 重新组织数据以便于写入
    category_data = []
    for category in category_quarterly.index:
        row = [category]
        for quarter in sorted(df['季度'].unique()):
            row.append(category_quarterly.loc[category, ('销售量', quarter)])
            row.append(category_quarterly.loc[category, ('销售额', quarter)])
        category_data.append(row)
    
    # 写入表头
    headers = ['类别']
    for quarter in sorted(df['季度'].unique()):
        headers.extend([f"Q{quarter}销量", f"Q{quarter}销售额"])
    
    for col_num, header in enumerate(headers, 1):
        cell = ws2.cell(row=1, column=col_num)
        cell.value = header
        cell.font = Font(bold=True)
        cell.fill = PatternFill("solid", fgColor="4F81BD")
        cell.alignment = Alignment(horizontal="center")
    
    # 写入数据
    for row_num, row_data in enumerate(category_data, 2):
        for col_num, value in enumerate(row_data, 1):
            cell = ws2.cell(row=row_num, column=col_num)
            cell.value = value
            if col_num % 2 == 0:  # 销量列
                cell.number_format = '#,##0'
            elif col_num % 2 == 1 and col_num > 1:  # 销售额列
                cell.number_format = '#,##0.00'
    
    # 创建折线图
    line_chart = LineChart()
    line_chart.title = "季度销售趋势"
    line_chart.x_axis.title = "季度"
    line_chart.y_axis.title = "销售额"
    
    # 设置图表数据范围(只取销售额列)
    quarters = len(df['季度'].unique())
    data = Reference(ws2, min_col=3, min_row=1, max_row=len(category_data)+1, max_col=2*quarters, min_col_offset=1)
    cats = Reference(ws2, min_col=1, min_row=2, max_row=len(category_data)+1)
    
    line_chart.add_data(data, titles_from_data=True)
    line_chart.set_categories(cats)
    
    # 添加图表到工作表
    ws2.add_chart(line_chart, "A" + str(len(category_data) + 3))
    
    # 调整列宽
    for ws in [ws1, ws2]:
        for col in range(1, ws.max_column + 1):
            ws.column_dimensions[get_column_letter(col)].width = 15
    
    # 保存工作簿
    wb.save(output_file)
    print(f"销售报表已生成: {output_file}")

# 使用示例
# generate_sales_report("原始销售数据.xlsx", "销售分析报表.xlsx")

场景二:库存管理系统

python 复制代码
import pandas as pd
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import datetime
import os

class InventoryManager:
    def __init__(self, inventory_file):
        """初始化库存管理系统"""
        self.inventory_file = inventory_file
        
        # 如果文件不存在,创建一个新的库存文件
        if not os.path.exists(inventory_file):
            self._create_new_inventory_file()
        
        # 加载库存数据
        self.load_inventory()
    
    def _create_new_inventory_file(self):
        """创建新的库存文件"""
        wb = Workbook()
        ws = wb.active
        ws.title = "库存"
        
        # 设置表头
        headers = ['产品ID', '产品名称', '类别', '供应商', '单价', '库存量', '库存价值', '最后更新']
        for col_num, header in enumerate(headers, 1):
            cell = ws.cell(row=1, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.fill = PatternFill("solid", fgColor="4F81BD")
            cell.alignment = Alignment(horizontal="center")
        
        # 设置示例数据
        sample_data = [
            [1001, '笔记本电脑', '电子产品', 'A供应商', 5999, 10, '=E2*F2', datetime.datetime.now()],
            [1002, '办公椅', '办公家具', 'B供应商', 899, 20, '=E3*F3', datetime.datetime.now()],
        ]
        
        for row_num, row_data in enumerate(sample_data, 2):
            for col_num, value in enumerate(row_data, 1):
                cell = ws.cell(row=row_num, column=col_num)
                cell.value = value
                if col_num == 5:  # 单价列
                    cell.number_format = '¥#,##0.00'
                elif col_num == 7:  # 库存价值列
                    cell.number_format = '¥#,##0.00'
                elif col_num == 8:  # 日期列
                    cell.number_format = 'yyyy-mm-dd hh:mm:ss'
        
        # 创建入库记录工作表
        ws_in = wb.create_sheet(title="入库记录")
        headers = ['记录ID', '产品ID', '产品名称', '入库数量', '单价', '总价值', '供应商', '入库日期', '操作人']
        for col_num, header in enumerate(headers, 1):
            cell = ws_in.cell(row=1, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.fill = PatternFill("solid", fgColor="4F81BD")
            cell.alignment = Alignment(horizontal="center")
        
        # 创建出库记录工作表
        ws_out = wb.create_sheet(title="出库记录")
        headers = ['记录ID', '产品ID', '产品名称', '出库数量', '单价', '总价值', '客户', '出库日期', '操作人']
        for col_num, header in enumerate(headers, 1):
            cell = ws_out.cell(row=1, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.fill = PatternFill("solid", fgColor="4F81BD")
            cell.alignment = Alignment(horizontal="center")
        
        # 调整所有工作表的列宽
        for ws in wb.worksheets:
            for col in range(1, len(headers) + 1):
                ws.column_dimensions[get_column_letter(col)].width = 15
        
        # 保存文件
        wb.save(self.inventory_file)
        print(f"已创建新的库存文件: {self.inventory_file}")
    
    def load_inventory(self):
        """加载库存数据"""
        # 使用pandas读取Excel文件的所有工作表
        self.inventory_df = pd.read_excel(self.inventory_file, sheet_name="库存")
        self.in_records_df = pd.read_excel(self.inventory_file, sheet_name="入库记录")
        self.out_records_df = pd.read_excel(self.inventory_file, sheet_name="出库记录")
        
        print("库存数据已加载")
        print(f"当前库存: {len(self.inventory_df)} 种产品")
        print(f"入库记录: {len(self.in_records_df)} 条")
        print(f"出库记录: {len(self.out_records_df)} 条")
    
    def add_product(self, product_id, name, category, supplier, price, quantity):
        """添加新产品到库存"""
        # 检查产品ID是否已存在
        if product_id in self.inventory_df['产品ID'].values:
            print(f"错误: 产品ID {product_id} 已存在")
            return False
        
        # 创建新产品记录
        new_product = {
            '产品ID': product_id,
            '产品名称': name,
            '类别': category,
            '供应商': supplier,
            '单价': price,
            '库存量': quantity,
            '库存价值': price * quantity,
            '最后更新': datetime.datetime.now()
        }
        
        # 添加到DataFrame
        self.inventory_df = self.inventory_df.append(new_product, ignore_index=True)
        
        # 添加入库记录
        in_record = {
            '记录ID': len(self.in_records_df) + 1,
            '产品ID': product_id,
            '产品名称': name,
            '入库数量': quantity,
            '单价': price,
            '总价值': price * quantity,
            '供应商': supplier,
            '入库日期': datetime.datetime.now(),
            '操作人': 'system'
        }
        self.in_records_df = self.in_records_df.append(in_record, ignore_index=True)
        
        # 保存更改
        self._save_to_excel()
        print(f"已添加新产品: {name} (ID: {product_id})")
        return True
    
    def update_stock(self, product_id, quantity_change, is_incoming=True, customer_or_supplier=None, operator='system'):
        """更新库存"""
        # 查找产品
        product_mask = self.inventory_df['产品ID'] == product_id
        if not any(product_mask):
            print(f"错误: 产品ID {product_id} 不存在")
            return False
        
        # 获取产品信息
        product_idx = product_mask.idxmax()
        product = self.inventory_df.loc[product_idx]
        
        # 计算新库存量
        new_quantity = product['库存量'] + quantity_change if is_incoming else product['库存量'] - quantity_change
        
        # 检查库存是否足够(出库时)
        if not is_incoming and new_quantity < 0:
            print(f"错误: 产品 {product['产品名称']} 库存不足,当前库存: {product['库存量']}")
            return False
        
        # 更新库存
        self.inventory_df.at[product_idx, '库存量'] = new_quantity
        self.inventory_df.at[product_idx, '库存价值'] = new_quantity * product['单价']
        self.inventory_df.at[product_idx, '最后更新'] = datetime.datetime.now()
        
        # 添加记录
        if is_incoming:
            # 入库记录
            record = {
                '记录ID': len(self.in_records_df) + 1,
                '产品ID': product_id,
                '产品名称': product['产品名称'],
                '入库数量': quantity_change,
                '单价': product['单价'],
                '总价值': quantity_change * product['单价'],
                '供应商': customer_or_supplier or product['供应商'],
                '入库日期': datetime.datetime.now(),
                '操作人': operator
            }
            self.in_records_df = self.in_records_df.append(record, ignore_index=True)
        else:
            # 出库记录
            record = {
                '记录ID': len(self.out_records_df) + 1,
                '产品ID': product_id,
                '产品名称': product['产品名称'],
                '出库数量': quantity_change,
                '单价': product['单价'],
                '总价值': quantity_change * product['单价'],
                '客户': customer_or_supplier or '未指定',
                '出库日期': datetime.datetime.now(),
                '操作人': operator
            }
            self.out_records_df = self.out_records_df.append(record, ignore_index=True)
        
        # 保存更改
        self._save_to_excel()
        action = "入库" if is_incoming else "出库"
        print(f"已{action} {product['产品名称']} {quantity_change} 个,当前库存: {new_quantity}")
        return True
    
    def generate_inventory_report(self, output_file):
        """生成库存报表"""
        # 创建一个新的工作簿
        wb = Workbook()
        ws = wb.active
        ws.title = "库存报表"
        
        # 添加报表标题
        ws.merge_cells('A1:H1')
        title_cell = ws['A1']
        title_cell.value = "库存状况报表"
        title_cell.font = Font(size=16, bold=True)
        title_cell.alignment = Alignment(horizontal="center")
        
        # 添加报表生成时间
        ws.merge_cells('A2:H2')
        date_cell = ws['A2']
        date_cell.value = f"生成时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        date_cell.alignment = Alignment(horizontal="center")
        
        # 添加表头
        headers = ['产品ID', '产品名称', '类别', '供应商', '单价', '库存量', '库存价值', '库存状态']
        for col_num, header in enumerate(headers, 1):
            cell = ws.cell(row=4, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.fill = PatternFill("solid", fgColor="4F81BD")
            cell.alignment = Alignment(horizontal="center")
        
        # 添加数据
        # 计算库存状态
        def get_stock_status(row):
            if row['库存量'] <= 0:
                return "缺货"
            elif row['库存量'] < 5:
                return "库存不足"
            elif row['库存量'] > 20:
                return "库存过多"
            else:
                return "正常"
        
        # 添加库存状态列
        self.inventory_df['库存状态'] = self.inventory_df.apply(get_stock_status, axis=1)
        
        # 按类别和库存状态排序
        sorted_df = self.inventory_df.sort_values(['类别', '库存状态'])
        
        # 写入数据
        for row_num, (_, row) in enumerate(sorted_df.iterrows(), 5):
            for col_num, column in enumerate(headers, 1):
                cell = ws.cell(row=row_num, column=col_num)
                value = row[column] if column in row else ""
                cell.value = value
                
                # 设置格式
                if column == '单价':
                    cell.number_format = '¥#,##0.00'
                elif column == '库存价值':
                    cell.number_format = '¥#,##0.00'
                
                # 设置库存状态的颜色
                if column == '库存状态':
                    if value == "缺货":
                        cell.fill = PatternFill("solid", fgColor="FF0000")
                    elif value == "库存不足":
                        cell.fill = PatternFill("solid", fgColor="FFC000")
                    elif value == "库存过多":
                        cell.fill = PatternFill("solid", fgColor="92D050")
        
        # 添加合计行
        total_row = len(sorted_df) + 5
        ws.cell(row=total_row, column=1).value = "合计"
        ws.cell(row=total_row, column=1).font = Font(bold=True)
        
        # 计算总库存量和总价值
        ws.cell(row=total_row, column=6).value = sorted_df['库存量'].sum()
        ws.cell(row=total_row, column=6).font = Font(bold=True)
        
        ws.cell(row=total_row, column=7).value = sorted_df['库存价值'].sum()
        ws.cell(row=total_row, column=7).font = Font(bold=True)
        ws.cell(row=total_row, column=7).number_format = '¥#,##0.00'
        
        # 添加类别统计
        ws.cell(row=total_row + 2, column=1).value = "类别统计"
        ws.cell(row=total_row + 2, column=1).font = Font(bold=True)
        
        category_stats = sorted_df.groupby('类别').agg({
            '产品ID': 'count',
            '库存量': 'sum',
            '库存价值': 'sum'
        }).reset_index()
        
        # 写入类别统计表头
        category_headers = ['类别', '产品数量', '总库存量', '总库存价值']
        for col_num, header in enumerate(category_headers, 1):
            cell = ws.cell(row=total_row + 3, column=col_num)
            cell.value = header
            cell.font = Font(bold=True)
            cell.fill = PatternFill("solid", fgColor="A5A5A5")
        
        # 写入类别统计数据
        for row_num, (_, row) in enumerate(category_stats.iterrows(), total_row + 4):
            ws.cell(row=row_num, column=1).value = row['类别']
            ws.cell(row=row_num, column=2).value = row['产品ID']
            ws.cell(row=row_num, column=3).value = row['库存量']
            ws.cell(row=row_num, column=4).value = row['库存价值']
            ws.cell(row=row_num, column=4).number_format = '¥#,##0.00'
        
        # 调整列宽
        for col in range(1, len(headers) + 1):
            ws.column_dimensions[get_column_letter(col)].width = 15
        
        # 保存报表
        wb.save(output_file)
        print(f"库存报表已生成: {output_file}")
        return output_file
    
    def _save_to_excel(self):
        """保存数据到Excel文件"""
        with pd.ExcelWriter(self.inventory_file, engine='openpyxl') as writer:
            self.inventory_df.to_excel(writer, sheet_name="库存", index=False)
            self.in_records_df.to_excel(writer, sheet_name="入库记录", index=False)
            self.out_records_df.to_excel(writer, sheet_name="出库记录", index=False)

# 使用示例
# inventory = InventoryManager("库存管理.xlsx")
# inventory.add_product(1003, "打印机", "办公设备", "C供应商", 1299, 5)
# inventory.update_stock(1001, 5, is_incoming=True, customer_or_supplier="A供应商", operator="张三")
# inventory.update_stock(1002, 2, is_incoming=False, customer_or_supplier="客户A", operator="李四")
# inventory.generate_inventory_report("库存报表.xlsx")

通过这些代码示例和实际应用场景,你可以轻松掌握Python Excel自动化的各种技巧,大幅提高工作效率。无论是简单的数据处理,还是复杂的报表生成,Python都能帮你轻松应对。

相关推荐
allenlluo4 分钟前
浅谈Web Components
前端·javascript
Mintopia4 分钟前
把猫咪装进 public/ 文件夹:Next.js 静态资源管理的魔幻漂流
前端·javascript·next.js
Spider_Man8 分钟前
预览一开,灵魂出窍!低代码平台的魔法剧场大揭秘🎩✨
前端·低代码·typescript
xianxin_9 分钟前
HTML 代码编写规范
前端
拾光拾趣录25 分钟前
🔥99%人只知WebP小,第3个特性却翻车?💥
前端·面试
xianxin_28 分钟前
HTML 5 本地数据库
前端
BUG收容所所长31 分钟前
如何用 Node.js 构建一个智能文章生成器?从零开始的技术实践之旅
前端·node.js·设计
breeze_whisper31 分钟前
使用AI聊天的时候,字儿是咋一个一个蹦出来的?
前端
前端Hardy32 分钟前
HTML&CSS&JS:有趣的练手小案例-开关灯效果
前端·javascript·css
Sawtone34 分钟前
[前端] 面试官:Babel = 比巴卜,想吃了是吧?👿一文速通 Babel 基础知识点,全文干货,干净不废话👋
前端·面试