Python利用openpyxl/xlrd/xlwt和pandas操作Excel

 更新时间:2026年03月18日 09:33:49   作者:倔强老吕  
Python提供了多个强大的库来操作Excel文件,主要包括openpyxl、xlrd/xlwt和pandas,下面将介绍这些库的基本用法,希望对大家有所帮助

Python提供了多个强大的库来操作Excel文件,主要包括openpyxlxlrd/xlwtpandas。下面将介绍这些库的基本用法。

1. 使用openpyxl操作Excel

openpyxl是一个流行的库,用于读写Excel 2010 xlsx/xlsm/xltx/xltm文件。

安装

pip install openpyxl

基本操作

from openpyxl import Workbook, load_workbook
# 创建新工作簿
wb = Workbook()
ws = wb.active  # 获取活动工作表
ws.title = "Sheet1"  # 设置工作表名称
# 写入数据
ws['A1'] = "姓名"
ws['B1'] = "年龄"
ws['A2'] = "张三"
ws['B2'] = 25
# 保存文件
wb.save("example.xlsx")
# 读取已有文件
wb = load_workbook("example.xlsx")
ws = wb.active
# 读取数据
for row in ws.iter_rows(values_only=True):
    print(row)
# 修改数据
ws['B2'] = 26
wb.save("example_modified.xlsx")

2. 使用pandas操作Excel

pandas是数据分析的强大工具,也提供了方便的Excel读写功能。

安装

pip install pandas openpyxl

基本操作

import pandas as pd

# 创建DataFrame
data = {
    '姓名': ['张三', '李四', '王五'],
    '年龄': [25, 30, 35],
    '城市': ['北京', '上海', '广州']
}
df = pd.DataFrame(data)

# 写入Excel
df.to_excel("pandas_example.xlsx", index=False, sheet_name='员工信息')

# 读取Excel
df_read = pd.read_excel("pandas_example.xlsx", sheet_name='员工信息')
print(df_read)

# 修改并保存
df_read['年龄'] = df_read['年龄'] + 1
df_read.to_excel("pandas_example_modified.xlsx", index=False)

3. 使用xlrd/xlwt操作旧版Excel

xlrd用于读取xls文件,xlwt用于写入xls文件(适用于Excel 2003及更早版本)。

安装

pip install xlrd xlwt

基本操作

import xlrd
import xlwt

# 写入xls文件
workbook = xlwt.Workbook()
sheet = workbook.add_sheet('Sheet1')

sheet.write(0, 0, '姓名')
sheet.write(0, 1, '年龄')
sheet.write(1, 0, '张三')
sheet.write(1, 1, 25)

workbook.save('old_example.xls')

# 读取xls文件
book = xlrd.open_workbook('old_example.xls')
sheet = book.sheet_by_index(0)

for row in range(sheet.nrows):
    print(sheet.row_values(row))

4. 高级操作

使用openpyxl处理样式

from openpyxl.styles import Font, Alignment, Border, Side

wb = Workbook()
ws = wb.active

# 设置单元格样式
ws['A1'].font = Font(bold=True, color="FF0000")
ws['A1'].alignment = Alignment(horizontal="center")
ws['A1'].border = Border(left=Side(style='thin'), 
                        right=Side(style='thin'), 
                        top=Side(style='thin'), 
                        bottom=Side(style='thin'))

wb.save("styled_example.xlsx")

使用pandas处理多个sheet

with pd.ExcelWriter('multi_sheet.xlsx') as writer:
    df1.to_excel(writer, sheet_name='Sheet1')
    df2.to_excel(writer, sheet_name='Sheet2')

5. 性能考虑

对于大型Excel文件:

  • 使用openpyxlread_only模式快速读取
  • 使用write_only模式快速写入
  • 考虑使用pandas进行批量操作
# 高性能读取
from openpyxl import load_workbook
wb = load_workbook(filename='large_file.xlsx', read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
    process(row)
    
# 高性能写入
from openpyxl import Workbook
wb = Workbook(write_only=True)
ws = wb.create_sheet()
for row in data:
    ws.append(row)
wb.save('large_output.xlsx')

总结

  • 对于简单的Excel操作,pandas提供了最简洁的API
  • 对于需要精细控制的Excel文件,openpyxl是更好的选择
  • 对于旧版Excel文件(xls),可以使用xlrd/xlwt

PyQt5创建Excel操作功能的桌面应用程序

下面将介绍如何使用PyQt5创建一个具有Excel操作功能的桌面应用程序,包括文件打开、数据展示、编辑和保存等功能。

环境准备

首先安装必要的库:

pip install PyQt5 pandas openpyxl xlrd

基本Excel查看器

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView, 
                             QFileDialog, QVBoxLayout, QWidget, 
                             QPushButton, QMessageBox)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
import pandas as pd
class ExcelViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Excel查看器")
        self.setGeometry(100, 100, 800, 600)
        self.initUI()
        self.current_file = None
    def initUI(self):
        # 主窗口部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        # 布局
        self.layout = QVBoxLayout()
        # 按钮
        self.btn_open = QPushButton("打开Excel文件")
        self.btn_open.clicked.connect(self.open_file)
        self.btn_save = QPushButton("保存修改")
        self.btn_save.clicked.connect(self.save_file)
        self.btn_save.setEnabled(False)
        # 表格视图
        self.table_view = QTableView()
        self.model = QStandardItemModel()
        self.table_view.setModel(self.model)
        # 添加到布局
        self.layout.addWidget(self.btn_open)
        self.layout.addWidget(self.btn_save)
        self.layout.addWidget(self.table_view)
        self.central_widget.setLayout(self.layout)
    def open_file(self):
        # 打开文件对话框
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开Excel文件", "", 
            "Excel Files (*.xlsx *.xls);;All Files (*)"
        )
        if file_path:
            try:
                # 使用pandas读取Excel文件
                self.df = pd.read_excel(file_path)
                self.current_file = file_path
                # 清除旧数据
                self.model.clear()
                # 设置表头
                self.model.setHorizontalHeaderLabels(self.df.columns)
                # 填充数据
                for row in range(self.df.shape[0]):
                    for col in range(self.df.shape[1]):
                        item = QStandardItem(str(self.df.iloc[row, col]))
                        item.setFlags(item.flags() ^ Qt.ItemIsEditable)  # 默认不可编辑
                        self.model.setItem(row, col, item)
                self.btn_save.setEnabled(True)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法打开文件:\n{str(e)}")
    def save_file(self):
        if not self.current_file:
            return
        try:
            # 从模型中获取修改后的数据
            rows = self.model.rowCount()
            cols = self.model.columnCount()
            data = []
            for row in range(rows):
                row_data = []
                for col in range(cols):
                    item = self.model.item(row, col)
                    row_data.append(item.text())
                data.append(row_data)
            # 创建DataFrame并保存
            new_df = pd.DataFrame(data, columns=self.df.columns)
            new_df.to_excel(self.current_file, index=False)
            QMessageBox.information(self, "成功", "文件保存成功!")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存失败:\n{str(e)}")
if __name__ == "__main__":
    app = QApplication(sys.argv)
    viewer = ExcelViewer()
    viewer.show()
    sys.exit(app.exec_())

增强版Excel编辑器

下面是一个功能更全面的Excel编辑器实现:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView, QFileDialog, 
                             QVBoxLayout, QHBoxLayout, QWidget, QPushButton, 
                             QMessageBox, QLabel, QLineEdit, QComboBox, QMenu, 
                             QAction, QInputDialog)
from PyQt5.QtCore import Qt, QModelIndex
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QKeySequence
import pandas as pd
class ExcelEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Excel编辑器")
        self.setGeometry(100, 100, 1000, 700)
        self.current_file = None
        self.df = None
        self.initUI()
    def initUI(self):
        # 主窗口部件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        # 主布局
        self.main_layout = QVBoxLayout()
        # 顶部工具栏
        self.toolbar_layout = QHBoxLayout()
        self.btn_open = QPushButton("打开")
        self.btn_open.setToolTip("打开Excel文件 (Ctrl+O)")
        self.btn_open.clicked.connect(self.open_file)
        self.btn_save = QPushButton("保存")
        self.btn_save.setToolTip("保存当前文件 (Ctrl+S)")
        self.btn_save.clicked.connect(self.save_file)
        self.btn_save.setEnabled(False)
        self.btn_save_as = QPushButton("另存为")
        self.btn_save_as.setToolTip("另存为新的Excel文件")
        self.btn_save_as.clicked.connect(self.save_file_as)
        self.btn_save_as.setEnabled(False)
        self.btn_add_row = QPushButton("添加行")
        self.btn_add_row.setToolTip("在表格末尾添加新行")
        self.btn_add_row.clicked.connect(self.add_row)
        self.btn_add_row.setEnabled(False)
        self.btn_delete_row = QPushButton("删除行")
        self.btn_delete_row.setToolTip("删除选中的行")
        self.btn_delete_row.clicked.connect(self.delete_row)
        self.btn_delete_row.setEnabled(False)
        self.file_label = QLabel("未打开文件")
        self.toolbar_layout.addWidget(self.btn_open)
        self.toolbar_layout.addWidget(self.btn_save)
        self.toolbar_layout.addWidget(self.btn_save_as)
        self.toolbar_layout.addWidget(self.btn_add_row)
        self.toolbar_layout.addWidget(self.btn_delete_row)
        self.toolbar_layout.addStretch()
        self.toolbar_layout.addWidget(self.file_label)
        # 表格视图
        self.table_view = QTableView()
        self.table_view.setSelectionBehavior(QTableView.SelectRows)
        self.table_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table_view.customContextMenuRequested.connect(self.show_context_menu)
        self.model = QStandardItemModel()
        self.table_view.setModel(self.model)
        # 搜索栏
        self.search_layout = QHBoxLayout()
        self.search_label = QLabel("搜索:")
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("输入搜索内容...")
        self.search_column = QComboBox()
        self.search_column.addItem("所有列")
        self.search_btn = QPushButton("搜索")
        self.search_btn.clicked.connect(self.search_data)
        self.search_layout.addWidget(self.search_label)
        self.search_layout.addWidget(self.search_input)
        self.search_layout.addWidget(self.search_column)
        self.search_layout.addWidget(self.search_btn)
        # 添加到主布局
        self.main_layout.addLayout(self.toolbar_layout)
        self.main_layout.addLayout(self.search_layout)
        self.main_layout.addWidget(self.table_view)
        self.central_widget.setLayout(self.main_layout)
        # 添加快捷键
        self.shortcut_open = QAction(self)
        self.shortcut_open.setShortcut(QKeySequence("Ctrl+O"))
        self.shortcut_open.triggered.connect(self.open_file)
        self.addAction(self.shortcut_open)
        self.shortcut_save = QAction(self)
        self.shortcut_save.setShortcut(QKeySequence("Ctrl+S"))
        self.shortcut_save.triggered.connect(self.save_file)
        self.addAction(self.shortcut_save)
        # 状态栏
        self.statusBar().showMessage("就绪")
    def open_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开Excel文件", "", 
            "Excel Files (*.xlsx *.xls *.csv);;All Files (*)"
        )
        if file_path:
            try:
                # 根据文件扩展名选择读取方式
                if file_path.endswith('.csv'):
                    self.df = pd.read_csv(file_path)
                else:
                    self.df = pd.read_excel(file_path)
                self.current_file = file_path
                self.file_label.setText(f"当前文件: {file_path}")
                # 更新搜索列下拉框
                self.search_column.clear()
                self.search_column.addItem("所有列")
                self.search_column.addItems(self.df.columns.tolist())
                # 加载数据到表格
                self.load_data_to_table()
                self.btn_save.setEnabled(True)
                self.btn_save_as.setEnabled(True)
                self.btn_add_row.setEnabled(True)
                self.btn_delete_row.setEnabled(True)
                self.statusBar().showMessage(f"成功加载文件: {file_path}", 3000)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法打开文件:\n{str(e)}")
                self.statusBar().showMessage(f"打开文件失败: {str(e)}", 5000)
    def load_data_to_table(self):
        # 清除旧数据
        self.model.clear()
        # 设置表头
        self.model.setHorizontalHeaderLabels(self.df.columns)
        # 填充数据
        for row in range(self.df.shape[0]):
            row_items = []
            for col in range(self.df.shape[1]):
                value = self.df.iloc[row, col]
                item = QStandardItem(str(value) if not pd.isna(value) else "")
                item.setEditable(True)
                row_items.append(item)
            self.model.appendRow(row_items)
        # 调整列宽
        self.table_view.resizeColumnsToContents()
    def save_file(self):
        if not self.current_file:
            return
        try:
            self.update_data_from_table()
            # 根据文件扩展名选择保存方式
            if self.current_file.endswith('.csv'):
                self.df.to_csv(self.current_file, index=False)
            else:
                self.df.to_excel(self.current_file, index=False)
            QMessageBox.information(self, "成功", "文件保存成功!")
            self.statusBar().showMessage("文件保存成功", 3000)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存失败:\n{str(e)}")
            self.statusBar().showMessage(f"保存失败: {str(e)}", 5000)
    def save_file_as(self):
        if self.df is None:
            return
        file_path, _ = QFileDialog.getSaveFileName(
            self, "另存为", "", 
            "Excel Files (*.xlsx);;CSV Files (*.csv);;All Files (*)"
        )
        if file_path:
            try:
                self.update_data_from_table()
                if file_path.endswith('.csv'):
                    self.df.to_csv(file_path, index=False)
                else:
                    # 确保扩展名是.xlsx
                    if not file_path.endswith('.xlsx'):
                        file_path += '.xlsx'
                    self.df.to_excel(file_path, index=False)
                self.current_file = file_path
                self.file_label.setText(f"当前文件: {file_path}")
                QMessageBox.information(self, "成功", f"文件已保存到:\n{file_path}")
                self.statusBar().showMessage(f"文件已保存到: {file_path}", 3000)
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存失败:\n{str(e)}")
                self.statusBar().showMessage(f"保存失败: {str(e)}", 5000)
    def update_data_from_table(self):
        # 从表格模型获取数据并更新DataFrame
        rows = self.model.rowCount()
        cols = self.model.columnCount()
        data = []
        for row in range(rows):
            row_data = []
            for col in range(cols):
                item = self.model.item(row, col)
                row_data.append(item.text() if item else "")
            data.append(row_data)
        self.df = pd.DataFrame(data, columns=self.df.columns)
    def add_row(self):
        if self.df is None:
            return
        # 在末尾添加空行
        empty_row = [""] * len(self.df.columns)
        self.model.appendRow([QStandardItem(item) for item in empty_row])
        # 滚动到最后一行
        self.table_view.scrollToBottom()
        self.statusBar().showMessage("已添加新行", 2000)
    def delete_row(self):
        selected = self.table_view.selectionModel().selectedRows()
        if not selected:
            QMessageBox.warning(self, "警告", "请先选择要删除的行")
            return
        # 从后往前删除以避免索引问题
        for index in sorted(selected, reverse=True):
            self.model.removeRow(index.row())
        self.statusBar().showMessage(f"已删除 {len(selected)} 行", 3000)
    def search_data(self):
        search_text = self.search_input.text().strip().lower()
        if not search_text:
            return
        column = self.search_column.currentText()
        column_idx = -1 if column == "所有列" else self.search_column.currentIndex() - 1
        # 清除之前的高亮
        for row in range(self.model.rowCount()):
            for col in range(self.model.columnCount()):
                item = self.model.item(row, col)
                if item:
                    item.setBackground(Qt.white)
        found = False
        for row in range(self.model.rowCount()):
            for col in range(self.model.columnCount()):
                if column_idx != -1 and col != column_idx:
                    continue
                item = self.model.item(row, col)
                if item and search_text in item.text().lower():
                    item.setBackground(Qt.yellow)
                    found = True
                    # 滚动到第一个匹配项
                    if not found:
                        self.table_view.scrollTo(self.model.index(row, col))
        if found:
            self.statusBar().showMessage(f"找到匹配 '{search_text}' 的内容", 3000)
        else:
            self.statusBar().showMessage(f"未找到匹配 '{search_text}' 的内容", 3000)
    def show_context_menu(self, position):
        if self.df is None:
            return
        menu = QMenu()
        edit_action = QAction("编辑单元格", self)
        edit_action.triggered.connect(self.edit_current_cell)
        menu.addAction(edit_action)
        add_row_action = QAction("添加行", self)
        add_row_action.triggered.connect(self.add_row)
        menu.addAction(add_row_action)
        delete_row_action = QAction("删除行", self)
        delete_row_action.triggered.connect(self.delete_row)
        menu.addAction(delete_row_action)
        menu.addSeparator()
        copy_action = QAction("复制", self)
        copy_action.triggered.connect(self.copy_selection)
        menu.addAction(copy_action)
        menu.exec_(self.table_view.viewport().mapToGlobal(position))
    def edit_current_cell(self):
        index = self.table_view.currentIndex()
        if not index.isValid():
            return
        old_value = self.model.itemFromIndex(index).text()
        new_value, ok = QInputDialog.getText(
            self, "编辑单元格", 
            f"输入新值 (原值: {old_value}):", 
            text=old_value
        )
        if ok and new_value != old_value:
            self.model.itemFromIndex(index).setText(new_value)
    def copy_selection(self):
        selected = self.table_view.selectionModel().selectedIndexes()
        if not selected:
            return
        # 获取选中的最小和最大行列
        min_row = min(index.row() for index in selected)
        max_row = max(index.row() for index in selected)
        min_col = min(index.column() for index in selected)
        max_col = max(index.column() for index in selected)
        # 构建文本
        text = ""
        for row in range(min_row, max_row + 1):
            row_text = []
            for col in range(min_col, max_col + 1):
                index = self.model.index(row, col)
                item = self.model.itemFromIndex(index)
                row_text.append(item.text() if item else "")
            text += "\t".join(row_text) + "\n"
        # 复制到剪贴板
        clipboard = QApplication.clipboard()
        clipboard.setText(text.strip())
        self.statusBar().showMessage("已复制选中内容到剪贴板", 2000)
if __name__ == "__main__":
    app = QApplication(sys.argv)
    editor = ExcelEditor()
    editor.show()
    sys.exit(app.exec_())

功能扩展

  • 数据筛选和排序:添加对表格数据的筛选和排序功能
  • 图表生成:集成matplotlib,从Excel数据生成图表
  • 公式支持:实现简单的Excel公式计算
  • 多工作表支持:处理包含多个工作表的Excel文件
  • 数据验证:添加输入数据验证功能
  • 样式设置:允许设置单元格字体、颜色、对齐方式等
  • 导入/导出:支持更多格式的导入导出,如JSON、SQL等
  • 宏录制:实现简单的操作记录和回放功能

程序发布

可以使用PyInstaller将应用打包为独立可执行文件:

pip install pyinstaller
pyinstaller --onefile --windowed excel_editor.py

这将在dist目录下生成可执行文件,可以在没有Python环境的电脑上运行。

这个实现提供了一个功能完整的Excel编辑器基础,您可以根据需要进一步扩展和完善功能。

到此这篇关于Python利用openpyxl/xlrd/xlwt和pandas操作Excel的文章就介绍到这了,更多相关Python操作Excel内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python打包生成so文件的实现

    python打包生成so文件的实现

    这篇文章主要介绍了python打包生成so文件的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Python异步编程之新旧协程的实现对比

    Python异步编程之新旧协程的实现对比

    Python中新旧协程的实现方式在协程发展史上有一段交集,并且旧协程基于生成器的协程语法让生成器和协程两个概念混淆,所以对学习者会造成一定的困扰,本文主要说明两种协程的实现方式的差异,需要的可以了解下
    2024-01-01
  • Python轻松实现将Excel数据批量导入数据库

    Python轻松实现将Excel数据批量导入数据库

    在日常数据处理工作中,将 Excel 文件内容导入数据库是一个常见需求,本文将基于轻量级 Excel 处理库完成 Excel 文件解析,结合 Python 内置的 SQLite 数据库,实现多工作表自动识别、动态创建表结构、批量数据入库的完整方案,希望对大家有所帮助
    2026-04-04
  • python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解

    python中print()函数的“,”与java中System.out.print()函数中的“+”功能详解

    python中的print()函数和java中的System.out.print()函数都有着打印字符串的功能。接下来通过本文给大家分享python中print()函数的“,”与java中System.out.print()函数中的“+”功能,需要的朋友参考下吧
    2017-11-11
  • Django中多种重定向方法使用详解

    Django中多种重定向方法使用详解

    这篇文章主要介绍了Django中多种重定向方法使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Python中多种字符串格式化的完整方法指南

    Python中多种字符串格式化的完整方法指南

    在日常编程中,我们经常需要将各种数据类型转换为美观、易读的字符串形式,Python提供了多种强大的字符串格式化方法,每种方法都有其适用场景,下面我们就来详细介绍一下吧
    2025-10-10
  • Python中schedule扩展的具体使用

    Python中schedule扩展的具体使用

    Python的schedule模块是一个轻量级的Python库,用于在指定时间执行某些操作,本文就来介绍一下Python中schedule扩展的具体使用,感兴趣的可以了解一下
    2024-12-12
  • 互斥锁解决 Python 中多线程共享全局变量的问题(推荐)

    互斥锁解决 Python 中多线程共享全局变量的问题(推荐)

    这篇文章主要介绍了互斥锁解决 Python 中多线程共享全局变量的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • python实现班级档案管理系统

    python实现班级档案管理系统

    这篇文章主要为大家详细介绍了python实现班级档案管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Django权限系统auth模块用法解读

    Django权限系统auth模块用法解读

    这篇文章主要介绍了Django权限系统auth模块用法解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05

最新评论