Python操作Excel工作簿(\*.xlsx)

前言

Excel 作爲流行的個人計算機數據處理軟件,混跡於各個領域,在程序員這裏也是常常被處理的對象,可以處理 Excel 格式文件的 Python 庫還是挺多的,比如 xlrdxlwtxlutilsopenpyxlxlwings 等等,但是每個庫處理 Excel 的方式不同,有些庫在處理時還會有一些侷限性。

接下來對比一下幾個庫的不同,然後主要記錄一下 xlwings 這個庫的使用,目前這是個人感覺使用起來比較方便的一個庫了,其他的幾個庫在使用過程中總是有這樣或那樣的問題,不過在特定情況下使用也是挺不錯的。

EXCEL文件

Excel 被稱爲電子表格,其實際可以保存的格式分爲很多種,但是“Excel 工作簿(*.xlsx)”和“Excel 97-2003 工作簿(*.xls)”是其中比較常用的兩種,可以認爲 .xls 格式的表格是 03版Excel 之前常用的格式,而 .xlsx 是 03版之後,一般指 07版Excel 之後常用的格式。

一般的 Excel 程序對於上述的兩種格式都可以打開編輯,也可以相互轉化存儲,不過還是建議在沒有特殊要求的情況下使用新版本的格式,一方面新的穩定版本可能會修復之前的一些BUG,同時也會帶來進行一些優化。

我也是在寫這篇總結之前才發現,一個空的 .xlsx 格式的文件大小有 7KB,而一個空的 .xls 格式的文件大小有 24KB,當我分別寫入一個相同的漢字後,兩個文件大小變成了 10KB 和 30KB,差距還是不小的,還有一個問題就是在將 .xlsx 格式的文件另存爲 .xls 格式時還會有兼容性提示,提醒用戶有些設置可能會丟失,所以能選新版本還是儘量用新版本吧。

測試環境

因爲很多應用程序是不斷迭代的,相對應的 Python 庫也是不斷迭代的,這裏儘可能的給出版本號,不同的版本可能會有不同的問題:

  • 操作系統: Windows 10 隨意版
  • Python: 3.75
  • xlrd: 1.2.0
  • xlwt: 1.3.0
  • xlutils: 2.0.0
  • openpyxl: 3.0.3
  • xlwings: 0.18.0

以上各個程序庫使用之前自行安裝就行,安裝方法就不贅述了,不過可以提供一個可以快速安裝鏡像源,使用 pip install -i https://pypi.doubanio.com/simple 庫名 可以儘可能解決下載安裝緩慢的問題。

Excel具體操作

關於使用 Python 具體操作 Excel 的方法可以分爲三組,配合使用 xlrdxlwtxlutils 操作作爲第一組,使用庫 openpyxl 作爲第二組,而 xlwings 作爲第三組,這篇總結重點總結 xlwings 的使用,其他兩組簡單瞭解。

xlrdxlwtxlutils

這一組操作 Excel 的庫名字很形象,一個讀、一個寫、一個小工具,湊到一起就可以對 Excel 肆意妄爲了,下面做個小練習,打開一個 Excel 文件然後修改第一個單元格的值,再另存爲一個新文件,代碼如下:

import xlrd
import xlwt
import xlutils.copy

def save_as_new_file(file_name, new_file_name):
    # 打開Excel文件
    rb = xlrd.open_workbook(file_name)
    # 創建一個可寫入的副本
    wb = xlutils.copy.copy(rb)
    # 獲得第一個sheet頁籤
    ws = wb.get_sheet(0)
    # 第一個單元格寫入測試值
    ws.write(0, 0, 'test value')
    # 另存爲一個新文件
    wb.save(new_file_name)

上述代碼無論是操作 .xlsx 文件還是操作 .xls 文件都不會報錯,但是另存爲的 .xlsx 格式的文件會打不開,同時你會發現正常存儲的 .xls 文件打開後格式全都沒了,怎麼辦,改個參數試試,將打開文件的代碼修改如下:

rb = xlrd.open_workbook(file_name, formatting_info=True)

其中參數 formatting_info=True 就表示打開Excel時保留原有的格式,但是這是相對於 .xls 格式的文件,對於 .xlsx 格式的文件直接跑出異常 raise NotImplementedError("formatting_info=True not yet implemented"),就因爲處理不了 .xlsx 格式的文件,我暫時沒有使用這幾個庫操作 Excel

還有一點,這幾個庫操作單元格時,行和列的索引是從0開始的。

openpyxl

首先說這個庫主要用來操作 .xlsx 格式的文件,對於 .xls 格式的文件無法打開,會報 openpyxl does not support the old .xls file format 這樣的錯誤,但是可以存儲成這樣的格式,再次打開時會有格式不匹配的警告,但是基礎的數據還在,所以還是優先用來操作 .xls 格式的文件吧。

寫一個新文件的常見用法:

from openpyxl import Workbook
from openpyxl import load_workbook
from openpyxl.styles import Font, Fill, Alignment, PatternFill

def write_new_excel(file_name):
    # 創建一個excel文檔
    wb = Workbook()
    # 獲得當前激活的sheet對象
    ws = wb.active
    # 給A2單元格賦值
    ws['A2'] = 'This is A2 cell'
    # 一行添加多列數據
    ws.append([1, 2, 'hello'])
    # 添加新的sheet
    ws = wb.create_sheet(title='NewInfo',index=0)
    # 設置單元格的值
    ws['A1'] = 'This is new sheet'

    # 保存excel
    wb.save(file_name)

讀取和改寫一個原有文件的常見用法:

def read_update_excel(file_name):
    # 加載Excel表
    wb = load_workbook(file_name)
    # 打印sheet數量
    print('sheet count:', len(wb.sheetnames))
    # 打印所有sheet名字
    print('sheet name list:', wb.sheetnames)
    # 獲取第一個sheet對象
    ws = wb[wb.sheetnames[0]]
    # 打印sheet錶行數和列數
    print('rows count:', ws.max_row, 'cols count:', ws.max_column)
    # 更新單元格A1的內容
    ws['A1'] = 'this is A1'
    # 在第二行位置插入一行
    ws.insert_rows(2)
    # 刪除第五行
    ws.delete_rows(5)
    # 獲取單元格對象,對應B2單元格
    cell = ws.cell(2,2)
    # 設置單元格內容
    cell.value = 'this is B2'
    # 修改字體格式爲粗體
    cell.font = Font(bold=True)
    # 修改單元格格式
    cell.fill = PatternFill("solid", fgColor="F0CDCD")

    # 保存原文件或另存一個文件
    wb.save(file_name)

使用這個庫遇到的情況,存儲帶有樣式的數據沒有發現問題,但是當加入一個計算公式後,另存爲一個文件時明顯文件尺寸變小了,但是數據和公式沒有發現有問題。

有資料說處理速度真的很慢,因爲我處理的文件比較小,但是沒有發現這方面的問題,還有一個問題就是說Excel中的宏全部丟失,這個測試的時候確實是丟了,只不過這個好像和文件格式有關,要想保存宏需要存儲爲 .xlsm 格式,但是 openpyxl 使用來操作 .xlsx 文件的,存儲時會導致宏丟失,強行存儲爲 .xlsm 格式會導致最終的文件打不開。

還有一點,這個庫操作單元格時,行和列的索引是從1開始的。

xlwings

這個庫在操作的首先要創建一個 App,通過這個創建出來的 App 對象來操作 Excel,非常像把 Excel 的各種操作 api 封裝到一起,然後通過這個 App 對象來調用,如果在創建 App 的時候不設置隱藏參數,是會正常打開 Excel 程序的。

使用 xlwings 的基本方式:

import xlwings as xw

# 設置Excel程序不可見
app = xw.App(visible=False, add_book=False)

# 通過 app 操作 Excel文件
# app.bala bala bala .....
# app.bala bala bala .....

# 優雅的退出
app.quit()

創建一個新的 Excel 文件並寫入數據:

def write_new_excel(app, file_name):
    # 創建新的 Excel 表
    wb = app.books.add()
    # 獲取當前活動的sheet
    ws = wb.sheets.active
    # 初始化二維區域的值
    arr_data = [[1, 2, 3], [4, 5, 6], [7, 8, 'end']]
    # 設置到新建的Excel中
    ws.range('A1:B3').value=arr_data
    # 設置單獨一個單元格的值
    ws.range('A4').value='this is A4'
    # 設置單獨一個單元格的值
    ws[3,1].value='this is B4'
    # 保存Excel文件
    wb.save(file_name)
    wb.close()

需要注意的是通過行索引和列索引修改單元格時,起始索引是0。

讀入已有 Excel 表格並修改

def read_update_excel(app, file_name):
    # 加載已有的表格
    load_wb = app.books.open(file_name)
    # 獲取Excel表中第一個sheet
    load_ws = load_wb.sheets[0]
    # 打印sheet的名字
    print(load_ws.name)
    # 根據sheet名字獲取sheet對象
    load_ws = load_wb.sheets[load_ws.name]
    # 獲取當前活動的sheet
    load_ws = load_wb.sheets.active

    # 獲取存在數據的行數和列數
    rows = load_ws.api.UsedRange.Rows.count
    cols = load_ws.api.UsedRange.Columns.count
    print('rows count:', rows, 'cols count:', cols)

    # 修改指定單元格數據(A1單元格)
    load_ws[0,0].value='this is A1'

    # 有空行或空列時獲取準確的行列數量
    print(load_ws.used_range.shape)

    # 從A1單元格開始擴展到非空行空列,最後的行數和列數
    print((load_ws.range('A1').expand().last_cell.row,
        load_ws.range('A1').expand().last_cell.column))

    # 從A1單元格開始擴展到非空行空列,最後的行數和列數
    print((load_ws.range('A1').expand().last_cell.row,
        load_ws.range('A1').expand().last_cell.column))

    # 從A1單元格開始擴展到非空行空列,最後形狀
    print(load_ws.range(1,1).expand().shape)

    # 從A1單元格開始擴展到非空行空列,最後的行數和列數
    print((load_ws.range('A1').expand('table').rows.count,
        load_ws.range('A1').expand('table').columns.count))

    # 保存修改後的Excel
    load_wb.save(file_name)
    load_wb.close()

Excel 增加刪除行和列

def insert_delete_rowscols(app, file_name):
    # 加載已有的表格
    load_wb = app.books.open(file_name)
    # 獲取當前活動的sheet
    load_ws = load_wb.sheets.active

    # 從第2行開始插入4行,也就是說2-5行變成新插入的空行
    load_ws.api.rows('2:5').insert
    # 刪除第6行和第7行
    load_ws.api.rows('6:7').delete
    # 插入一個單元格,實際測試效果是B列從B2開始向下移動,B2爲新添加的單元格
    load_ws.range('B2').api.insert
    # 插入新的一列
    load_ws.api.columns('B').insert
    # 刪除一列
    load_ws.api.columns('C').delete

    # 保存修改後的Excel
    load_wb.save(file_name)
    load_wb.close()

單元格寬高查詢設置與合併

def cell_operation(app, file_name):
    # 加載已有的表格
    load_wb = app.books.open(FILE_PATH_ROOT + file_name)
    # 獲取當前活動的sheet
    load_ws = load_wb.sheets.active

    # 合併單元格
    load_ws.range('A2:A3').api.merge

    #獲取單元格
    cell = xw.Range('B2')
    # 打印單元格所在的行和列
    print("row is:", cell.row, "col is:", cell.column)

    # 打印當前格子的高度和寬度
    print("cell.width:", cell.width, "cell.height:", cell.height)

    # 設置當前格子的高度和寬度
    cell.row_height = 32
    cell.column_width = 64

    # 指定單元格的高度和寬度自適應
    cell.columns.autofit()
    cell.rows.autofit()

    # 再次打印當前格子的高度和寬度
    print("cell.width:", cell.width, "cell.height:", cell.height)

    # 保存修改後的Excel
    load_wb.save(file_name)
    load_wb.close()

幾個庫支持情況對比

雖然前面寫了這麼多方法,但是遇到一個實際的問題時還是會猶豫,到底用哪種方式呢?下面做一個簡單的對比,只是根據我做的實驗來簡單對比,如果有不準確甚至是錯誤的地方,歡迎大家指出來,我會盡快改正的。

情景/庫 xlrdxlwtxlutils openpyxl xlwings
讀取.xls 可以帶有樣式讀取 不支持 可以讀取
保存.xls 可以帶有樣式保存 可以保存,但是提示文件擴展名不匹配,可以看到原始數據 可以保存,但是提示文件擴展名不匹配,可以看到原始數據
讀取.xlsx 可以讀取,但沒有樣式 可以帶有樣式讀取 可以帶有樣式讀取
保存.xlsx 保存後打不開 可以帶有樣式保存 可以帶有樣式保存
讀取.xlsm 可以讀取,但沒有樣式和宏 可以讀取,但沒有宏 可以讀取包含宏的表格
保存.xlsm 保存後打不開,存成 .xls 格式宏丟失 保存後打不開,存成 .xls想 格式宏丟失 存儲後宏還在
增刪行和列 沒有直接方法 支持 支持
另存後大小 .xls 文件沒有變化 .xlsx 文件會變小 .xls.xlsx 文件沒有變化
使用建議 只操作.xls文件可以考慮 只操作.xlsx文件可以考慮,不能帶有宏 一個比較好的選擇,使用時感覺速度稍微有點慢

總結

  1. Excel 表格程序經過版本的更替發生了很大的變化,出現了相同內容時 .xls.xlsx 格式的文件大很多的情況
  2. 基於上一點考慮,如果能使用的新版的表格,那麼就放棄舊的格式的吧
  3. 還有一個神奇的情況,一個帶有少量數據的 .xlsx 格式的表格要比一個空表格還要小,這是什麼情況,暫時沒弄明白怎麼回事,求知道的大神告知一二
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章