IT技术博客大学习 共学习 共进步

Python操作Excel

Solrex Shuffling 2012-09-02 20:25:26 浏览 4,922 次

    老婆单位有时候有一些很大的 Excel 统计报表需要处理,其中最恶心的是跨表的 JOIN 查询。他们通常采取的做法是,把多个 Excel 工作簿合成一个工作簿的多个表格,然后再跑函数(VLOOKUP之类)去查。因为用的函数效率很低,在 CPU 打满的情况下还要跑几个小时。

    然后我就看不过去了,我也不懂 Excel,不知道如何优化,但我想用 Python+SQLite 总归是能够实现的。于是就尝试了一把,效果还不错,一分钟以内完成统计很轻松,其中大部分时间主要花在读 Excel 内容上。

1. Python 操作 Excel 的函数库

    我主要尝试了 3 种读写 Excel 的方法:

    1> xlrd, xlwt, xlutils: 这三个库的好处是不需要其它支持,在任何操作系统上都可以使用。xlrd 可以读取 .xls, .xlsx 文件,非常好用;但因为 xlwt 不能直接修改 Excel 文档,必须得复制一份然后另存为其它文件,而且据说写复杂格式的 Excel 文件会出现问题,所以我没有选它来写 Excel 文件。

    2> openpyxl: 这个库也是不需要其它支持的,而且据说对 Office 2007 格式支持得更好。遗憾地是,我经过测试,发现它加载 Excel 文件的效率比 xlrd 慢 3 倍以上,内存使用在 10 倍以上,于是就放弃了。

    3> win32com: Python Win32 扩展,这个库需要运行环境为 Windows+Office 对应版本。由于 Python Win32 扩展只是把 COM 接口包装了一下,可以视为与 VBA 完全相同,不会有读写格式上的问题。尝试了一下用 win32com 读取 Excel 文件,效率还是比 xlrd 慢一些。

    由于读取效率上 xlrd > win32com > openpyxl,所以我自然选择了 xlrd 用来读取统计报表;而最终输出的报表格式较复杂,所以选择了 win32com 直接操作 Excel 文件。

2. Python 里的关系型数据库

    SQLite 是一个非常轻量级的关系型数据库,很多语言和平台都内置 SQLite 支持,也是 iOS 和 Android 上的默认数据库。Python 的标准库里也包含了 sqlite3 库,用起来非常方便。

3. 用 xlrd 读取 Excel 并插入数据库样例

    如果数据量不大,直接用 Python 内部数据结构如 dict, list 就够了。但如果读取的几张表数据量都较大,增加个将数据插入数据库的预处理过程就有很大好处。一是避免每次调试都要进行耗时较长的 Excel 文件载入过程;二是能充分利用数据库的索引和 SQL 语句强大功能进行快速数据分析。

    #!/usr/bin/python

     # -*- coding: gbk -*-

    import xlrd

     import sqlite3

    # 打开数据库文件

     device_city_db = sqlite3.connect('device_city.db')

     cursor = device_city_db.cursor()

    # 建表

     cursor.execute('DROP TABLE IF EXISTS device_city')

     cursor.execute('CREATE TABLE device_city (device_id char(16) PRIMARY KEY, city varchar(16))')

     # 打开 device 相关输入 Excel 文件

     device_workbook = xlrd.open_workbook('输入.xlsx')

     device_sheet = device_workbook.sheet_by_name('设备表')

    # 逐行读取 device-城市 映射文件,并将指定的列插入数据库

     for row in range(1, device_sheet.nrows):

        device_id = device_sheet.cell(row, 6).value

        if len(device_id) > 16:

            device_id = device_id[0:16]

        if len(device_id) == 0:

            continue

        city = device_sheet.cell(row, 10).value

        # 避免插入重复记录

        cursor.execute('SELECT * FROM device_city WHERE device_id=?', (device_id,))

        res = cursor.fetchone()

        if res == None:

            cursor.execute('INSERT INTO device_city (device_id, city) VALUES (?, ?)',

                           (device_id, city))

        else:

            if res[1] != city:

                print '%s, %s, %s, %s' % (device_id, city, res[0], res[1])

     device_city_db.commit()

4. 将结果写入 Excel 文件样例

    使用 win32com 写入 Excel 的时候要注意,一定要记得退出 Excel,否则下次运行会出错。这需要增加异常处理语句,我这里偷了个懒,出了异常后要手动杀死任务管理器中的 excel 进程。至于 win32com 中类的接口,可以从 MSDN 网站查阅。

    import win32com.client as win32

     import os

     excel = win32.gencache.EnsureDispatch('Excel.Application')

     excel.Visible = False

     # 貌似这里只能接受全路径

     workbook = excel.Workbooks.Open(os.path.join(os.getcwd(), '输出.xlsx'))

     month_sheet = workbook.Worksheets(1)

     # 计算文件中实际有内容的行数

     nrows = month_sheet.Range('A65536').End(win32.constants.xlUp).Row

     # 操作 Excel 单元格的值

     for row in range(5, nrows-4):

        month_sheet.Cells(row, 1).Value += something

     # 保存工作簿

     workbook.Save()

     # 退出 Excel

     excel.Application.Quit()

建议继续学习

  1. JQuery实现Excel表格呈现 (阅读 48,164)
  2. 神马?用excel来做项目管理? (阅读 43,160)
  3. perl的写excel文件 (阅读 3,740)
  4. php导出excel文件 (阅读 3,441)
  5. PHP导出MySQL数据到Excel文件 (阅读 3,320)
  6. PHP导出MySQL数据到Excel文件 (阅读 3,061)
  7. excel打开csv文件乱码的解决方法 (阅读 2,640)