Python 二次開發 Excel 簡介



阿陽的 Excel 二次開發簡明教程,本文僅用於個人學習,除此之外,無其他任何用途。


因個人能力有限,本文難免有所疏漏/錯誤,不妥之處還請各位批評指正。


一、前沿


  截止目前,可用於二次開發 Excel 的 Python 庫主要有:xlwings、openpyxl、pandas、win32com、Xlsxwriter、DataNitro 和 xlutils 等。各模塊的性能對比,詳見博客:Python讀寫Excel文件第三方庫彙總,你想要的都在這兒!


在這裏插入圖片描述

環境配置及可實現操作


  綜合各方面資料,可得到如下結論:僅從讀寫性能上考慮,win32com 模塊性能最優。


  因此,本文利用 win32com 模塊二次開發 Excel。一方面是因爲讀寫速度快,另一方面該模塊直接調用 Excel 本身的各種函數,win32com 僅僅只起到接口作用,即它只是構建起 Python 與 Excel 間的聯繫,各種功能的實現需直接查看 Excel 官方的幫助文檔尋找相應的函數。而其他庫大都是內部先定義一些轉換函數,以降低用戶的使用難度,然後再發送給 Excel 執行,所以讀寫速度上會有所降低。(我是這麼理解的,不知道對不對,後續再細研究吧)


  win32com 模塊是一個處理 windows 應用的擴展,調用 Excel 只是該庫能實現的一小部分功能,利用該庫用戶還可以調用 windows 平臺上的其他各種應用程序,如 Word、AutoCAD、SAP2000、Abaqus 等等。因此,充分利用好該庫可進行很多軟件的二次開發。


  • Python 二次開發 AutoCAD 簡介

  • Python 二次開發 SAP2000 概述

  • Abaqus 二次開發 基本概念

  • nCodeDL 疲勞計算 基本流程

  • Python 二次開發 Word 簡介


  採用 win32com 模塊進行 Excel 二次開發,初期學習成本相對較高 (不過也沒高多少),需要隨時查看龐雜的 Excel 二次開發幫助文檔,但這有助於更深刻的理解 Excel 的程序架構,而且還能實現更多的功能。


  其他庫雖然上手容易,但這都是第三方給你定義好的調用規則,用戶和 Excel 間的交互要在這些庫內部進行信息中轉,然後庫的後臺完全按照官方幫助文檔中定義的規則完成 Excel 的調用。這雖然降低了用戶的使用難度,但顯然學習這些庫的使用規則沒有更多的通用性和意義,即你無法將你對這個庫的瞭解推廣到其他應用程序的調用上,相當於學習了個二手知識。這種學習方式不是不對,而是瞭解的不全面。當遇到該庫實現不了的功能,你可能會手足無措,無從下手。而且庫的更新肯定會滯後Excel 的更新,這也爲及時適配最新版本的 Excel 帶來了些許麻煩。


  當然,你只是想簡簡單單的實現 Python 對 Excel 的調用,除此也沒什麼特別的需求,那以上各種庫就可以看心情,想用哪個用哪個。若想實現各種應用程序的大串聯,又不想學習太多的庫的使用規則,那麼 win32com 是絕對值得選擇的。


  值得注意的是,該庫不單獨存在,可通過安裝 pywin32 獲取。


  以上純屬個人理解,如有錯誤請各位批評指正。



1.1 pywin32庫


  pywin32 是一個 Python 庫,爲 Python 提供訪問 Windows API 的擴展,提供了齊全的 windows 常量、接口、線程以及COM 機制等,瞭解更多詳見博客:如何利用Python和win32編程避免重複性體力勞動(一)


  如已安裝 Anaconda,在 Anaconda Prompt 中鍵入 pip install pywin32 便可自動完成該庫的導入;如未安裝 Anaconda,也可 pip 手動導入,其在PyPI (Python Package Index)上的註冊地址爲: https://pypi.org/project/pywin32


  本文通過 pywin32 庫調用 Excel 程序,本文中的 Excel 版本爲 Excel 2016,其餘版本的調用類似。


  COM (Component Object Model,組件對象模型) 是微軟公司於1993年提出的一種組件技術,它是一種平臺無關、語言中立、位置透明、支持網絡的中間件技術。


  組件對象模型,一種面向對象的編程模式。它定義了對象在單個應用程序內部或多個應用程序之間的行爲方式。它是微軟對於網頁服務器與客戶端、增益集與 Office 系列軟件之間交互的一項軟件組件技術。


在這裏插入圖片描述

Excel表格的組成


1.2 幫助文檔


  Excel 二次開發的有關信息可在如下兩個官方網站中查看:


     - Microsoft: office 開發人員中心 Excel

     - GitHub:MicrosoftDocs/VBA-Docs/excel/Concepts



1.3 枚舉


  Excel 中很多功能的實現都離不開枚舉,如設置文本居中,選擇另存爲文件的格式等等,各種枚舉類型可在 Office 開發人員中心 / 枚舉 (Excel) 中查詢。


  1. 常量枚舉
名稱 說明
xlCenter -4108 居中
  1. XlFileFormat 枚舉
名稱 說明 拓展名
xlCSV 6 CSV *.csv
xlExcel8 56 Excel 97-2003工作簿 *.xls
xlWorkbookDefault 51 默認工作簿 *.xlsx

在這裏插入圖片描述

Excel 中的枚舉


1.4 顏色


  Excel 內部的顏色索引如下圖所示:

在這裏插入圖片描述

  有些情況需要採用非內部定義的顏色索引指定顏色,如邊框顏色的指定。此時,則按如下方式設置顏色:Object.Color = 16711935,16711935 爲洋紅色的標識號,顏色標識號 color 由顏色的 RGB 值換算而來,具體換算規則如下:


color=R+256×G+256×256×B {color} = R + 256 × G + 256 × 256 × B


  例如:對於白色(R=255,G=255,B=255),其對應的顏色標識號 color 爲 255 + 256 * 255 + 256 * 256 * 255 = 16777215。


  常用標準顏色代號如下:


顏色 color 顏色 color
黑色 0 青色 16776960
紅色 255 藍色 16711680
黃色 65535 洋紅 16711935
綠色 65280 白色 16777215

  該顏色定義方式與 SAP2000 二次開發中關於顏色的定義方式相一致,如想了解更多,詳見博客 【Python 二次開發 SAP2000 概述 】。



二、與 Excel 的連接


# -*- coding: utf-8 -*-

"""
    =============================
    Author: DalNur
    Email: [email protected]
    =============================
"""

import win32com.client
import os

xlApp = win32com.client.Dispatch("Excel.Application")
xlApp.DisplayAlerts = False		# 關閉警告
xlApp.Visible = True			# 程序可見

  當批處理數據時,沒必要顯示 Excel 程序界面,可將其設置爲後臺運行,代碼如下:

xlApp.DisplayAlerts = False		# 關閉警告
xlApp.Visible = False			# 程序不可見

  若上述設置無效,Excel 仍然顯示界面,那麼說明在代碼運行前你的電腦上已運行着 Excel 進程,此時,需要手動將其關閉,然後再運行 Python 代碼。


在這裏插入圖片描述

Excel進程的關閉


三、工作簿 Workbook


3.1 打開/新建


path = r"D:\PyExcel\test.xlsx"

dirct = os.path.split(path)[0]				# 獲取文件夾路徑 D:\PyExcel
fileName = os.path.split(path)[1]			# 獲取文件名 test.xlsx
ext = os.path.splitext(path)[1]				# 獲取文件拓展名 .xlsx
fileIsExist = os.path.exists(path)			# 判斷文件是否存在

# 若文件夾不存在則新建

if not os.path.exists(dirct):
    try:
        os.makedirs(dirct)
    except OSError:
        pass

# 若文件不存在則新建

if not fileIsExist:
    format = {".xlsx": 51, ".xls": 56, ".csv": 6}
    myFormat = format[ext] if ext in format else 51
    # 根據拓展名確定文件格式標識碼,默認格式爲.xlsx,其標識碼爲51。
    
    wb = xlApp.Workbooks.Add()			# 新建工作簿
    wb.SaveAs(path, myFormat)			# 文件另存爲

xlBook = xlApp.Workbooks.Open(path)  # 打開工作簿

在這裏插入圖片描述

代碼運行效果


四、工作表 Worksheet


4.1 新建


  1. 新建自定義名稱工作表
mySheetName = "Sheet3"

try:
    # 確定工作表 mySheetName 是否存在
    xlBook.Worksheets(mySheetName).Name
    print(f"""The sheet named "{mySheetName}" DOES exist in this workbook.""")
    
except:
    # 工作表 mySheetName 不存在則新建
    xlBook.Worksheets.Add().Name = mySheetName
    print(f"""The sheet named "{mySheetName}" did not exist in this workbook but it has been created now.""")

  按此方式新建的工作表將出現在當前激活工作表的左側,即運行上述代碼新建的工作表 “Sheet3” 將位於 “Sheet1” 的左側,且新建的工作表 “Sheet3” 處於當前活躍狀態。


在這裏插入圖片描述


  1. 新建自定義位置工作表
location = 2  # 新建工作表的位置,從左向右數。

mySheetName = "Sheet5"

if location > 1:

    try:
        afterSheet = xlBook.Worksheets(location - 1)
        # 判斷location是否大於工作簿中現有工作表總數
    except:
        afterSheet = xlBook.Worksheets(xlBook.Worksheets.Count)

    sht = xlBook.Worksheets.Add(Before=None, After=afterSheet)
    
else:
    beforeSheet = xlBook.Worksheets(1)
    sht = xlBook.Worksheets.Add(Before=None)

sht.Name = mySheetName

  由於 location = 2 ,則新建的工作表 “Sheet5”,位於左起第二個位置處。


在這裏插入圖片描述


4.2 刪除/替換


# xlBook.Worksheets.Add().Name = "Sheet4"

mySheetName = "Sheet4"

try:
    xlBook.Worksheets(mySheetName).Delete()
    xlBook.Worksheets.Add().Name = mySheetName
    print(f"""The sheet named "{mySheetName}" has been replaced.""")
except:
    print(f"""The sheet named "{mySheetName}" did NOT exist in this workbook.""")
    xlBook.Worksheets.Add().Name = mySheetName
    print(f"""The sheet named "{mySheetName}" has been added to this workbook.""")

  該部分代碼將刪除指定名稱的工作表,創建同名的新工作表,以此方式實現工作表的替換,若原工作簿中不存在指定該名稱的工作表則新建。


4.3 引用


  1. 按索引號引用工作表

  索引號是基於工作表標籤在同一類型的工作表中的位置(按從左到右的方式計數)分配給工作表的序號。以下代碼激活活動工作簿中的第一張工作表。

indexNum = 1
sht = xlBook.Worksheets(indexNum).Activate()

# totalNum = xlBook.Worksheets.Count
# indexNum <= totalNum

  值得注意的是工作表的索引號起 始於 1 而非編程中常見的起始於 0,即索引號是幾就引用第幾張工作表(按左起計)。另外,索引號只能爲 不大於 當前工作簿中工作表總數的正整數,也就是說 indexNum = -1 不能表示引用最後一張工作表。


  1. 通過名稱引用工作表
mySheetName = "Sheet4"
sht = xlBook.Worksheets(mySheetName).Activate()


五、單元格 Cell


xlBook.Worksheets("Sheet1").Activate()
sht = xlBook.Worksheets("Sheet1")

5.1 引用


  1. 使用 A1 表示法引用單元格

  按如下方式引用 “D4” 單元格:

cell1 = sht.Range("D4")  # 引用 "D4" 單元格

  1. 索引號引用單元格

  按如下方式引用第4行第2列的單元格:

row, col = 4, 2
cell2 = sht.Cells(row, col)

  在 Excel 中,單元格的行號和列號也均從 從1起計數


  1. 相對於其他單元格引用單元格

  按如下方式引用相對於活躍單元格指定行數和列數的其他單元格:

# 指定 "F4" 爲當前活躍/引用單元格
activeCell = sht.Range("F4")
activeCell.Value = "Active Cell"

# 引用 "I5" 單元格
offSetRow, offSetCol = 1, 3
cell3 = activeCell.Offset(offSetRow+1, offSetCol+1)

# 引用 "C2" 單元格
offSetRow, offSetCol = -2, -3
cell4 = activeCell.Offset(offSetRow+1, offSetCol+1)

其中,offSetRow、offSetCol 分別爲相對於當前引用單元格偏置的行數和列數,以向下向右爲正,向左向上爲負。


5.2 讀寫


  1. 寫入數據
cell1.Value = "from python"  
cell2.Value = 1024
cell3.Value = "Hello Cell I5"
cell4.Value = "Hello Cell C2"

在這裏插入圖片描述

代碼運行效果

  1. 讀取數據
row, col = 2, 3
cellVal = sht.Cells(row, col).Value
print(cellVal)

cellVal = sht.Range("B4").Value
print(cellVal)

cellVal = sht.Range("E3").Value
print(cellVal)

在這裏插入圖片描述


5.3 公式


sht.Range("B7").Value = 2
sht.Range("C7").Value = 3

sht.Range("E6").Formula = "=B7^C7"
sht.Range("E7").Formula = "=B7+C7"
sht.Range("E8").Formula = "=B7/C7"


在這裏插入圖片描述

公式設置結果

5.4 字體


cellVal = sht.Range("B12").Value = "Font Test" 
print(sht.Range("B12").Font.Name)
  1. 字體
cell = sht.Range("C12")
cell.Value = "Font Test"
cell.Font.Name = "Times New Roman"  # 字體名稱
  1. 加粗
cell = sht.Range("D12")
cell.Value = "Font Test"
cell.Font.Bold = True  # 加粗
  1. 傾斜
cell = sht.Range("E12")
cell.Value = "Font Test"
cell.Font.Italic = True  # 傾斜
  1. 顏色
cell = sht.Range("F12")
cell.Value = "Font Test"
cell.Font.ColorIndex = 3  # 紅色字體

5.5 格式


  1. 填充
cell = sht.Range("G12")
cell.Value = "Font Test"
cell.Interior.ColorIndex = 5  # 藍色背景色
  1. 對齊
cell = sht.Range("H12")
cell.Value = "Font Test"

cell.HorizontalAlignment = -4108  # 水平居中 XlHAlign 枚舉 指定對象的水平對齊方式
  1. 邊框
cell = sht.Range("I12")
cell.Value = "Font Test"

cell.Borders.LineStyle = -4119  # 雙線條紋 XlLineStyle 枚舉 指定邊框的線條樣式
cell.Borders(9).Color = 16711935 # 底紋洋紅 XlBordersIndex 枚舉 指定要檢索的邊框

在這裏插入圖片描述

單元格字體及格式設置結果

六、區域 Range


6.1 引用


6.2 格式


6.3 選擇


七、文件


7.1 保存

xlBook.Save()  # 保存工作簿

7.2 另存爲

newPath = r"D:\PyExcel\test.xls"				# 另存爲文件的絕對路徑
newDirct = os.path.split(newPath)[0]			# 獲取文件夾路徑 D:\PyExcel
newFileName = os.path.split(newPath)[1]			# 獲取文件名 test.xls
ext = os.path.splitext(newPath)[1]				# 獲取文件拓展名 .xls
fileIsExist = os.path.exists(newPath)			# 判斷文件是否存在

# 若文件夾不存在則新建

if not os.path.exists(newDirct):
    try:
        os.makedirs(newDirct)
    except OSError:
        pass

# 若文件存在則不另存爲

if not fileIsExist:
    format = {".xlsx": 51, ".xls": 56, ".csv": 6}
    myFormat = format[ext] if ext in format else 51
    # 根據拓展名確定文件格式標識碼,默認格式爲.xlsx,其標識碼爲51。
    wb.SaveAs(newPath, myFormat)			# 文件另存爲
    print(f"""The file named "{newPath}" has been saved.""")
else:
    print(f"""Failed to save as ! The file named "{newPath}" DOES exist.""")
    xlBook.Save()  # 保存工作簿

在這裏插入圖片描述

另存爲結果

7.3 關閉

xlBook.Close()  # 關閉工作表

7.4 退出

xlApp.Quit()  # 退出程序

八、格式轉換


8.1 .xlsx/.xls/.csv 文件轉換爲 .txt 文件

def convertExcelToTxt(excelPath, sheetName=None, txtPath=None):
    """文本文件轉換爲Excel文件
    :param excelPath: Excel文件的絕對路徑,e.g. excelPath = r"D:\PyExcel\test.xls";
    :param txtPath: 生成的Excel文件的絕對路徑,,e.g. txtPath = r"D:\PyExcel\Mytest.txt";
                    txtPath=None,表示不特意指定txt的文件名稱,系統採用Excel文件名稱+工作表名命名txt文件。
    :param sheetName: 工作表名稱,若不指定,則爲當前活躍工作表。
    """

    import win32com.client
    import os

    xlApp = win32com.client.Dispatch("Excel.Application")
    xlApp.DisplayAlerts = False			# 關閉警告
    xlApp.Visible = False				# 程序不可見

    try:
        xlBook = xlApp.Workbooks.Open(excelPath)  # 打開工作簿
    except OSError:
        print(f"""The file named "{excelPath}" does Not exists.""")

    if sheetName is None:
        sht = xlBook.ActiveSheet
    else:
        sht = xlBook.Worksheets(sheetName)

    shtName = sht.Name

    if txtPath is None:
        temp = os.path.splitext(excelPath)[0]  # 不含拓展名的文件路徑+文件名稱
        ext = ".txt"
        txtPath = temp + "_" + shtName + ext

    row = sht.UsedRange.Rows.Count  # 有用區域總行數
    col = sht.UsedRange.Columns.Count  # 有用區域總列數

    with open(txtPath, "w")as ftxt:
        for i in range(row + 1)[1:]:
            allRow = []
            for j in range(col + 1)[1:]:
                cellValue = sht.UsedRange.Cells(i, j).Value
                if cellValue is None:
                    # pass
                    allRow.append("\t")  # 無數據位置處用製表符替代
                else:
                    cellValue = str(cellValue)
                    allRow.append(cellValue)
                allRow.append("\t")  # 無數據位置處用製表符替代
            allRow.append("\n")  # 添加換行符號
            print(allRow)
            ftxt.writelines(allRow)

    # sht.UsedRange.Select()  # 選擇已用區域(其中包括不含數據的帶格式的單元格)
    # temp = sht.UsedRange.Address  # 已用區域起止地址,即$C$8:$S$161。
    # cell = sht.UsedRange.Cells(1, 1)  # 獲取單個單元格數值

    xlBook.Close()
    xlApp.Quit()

    return print(f"""The file named "{txtPath}" has been created.""")


if __name__ == "__main__":
    # 測試代碼
    import time
    start = time.time()

    convertExcelToTxt(excelPath=r"D:\PyExcel\Mytest.xlsx")
    # 特別注意:文件名不能寫成 "D:/PyExcel/test.txt",否則報錯。
    convertExcelToTxt(excelPath=r"D:\PyExcel\Mytest.xlsx", sheetName="Sheet2")
    end = time.time()
    dur = end - start
    dur = round(dur, 2)  # 保留2位小數
    print("Total time:", dur, "s.")  # print("It takes", dur, "s totally.")

8.2 .txt 文件轉換爲 .xlsx/.xls/.csv 文件

def convertTxtToExcel(txtPath, excelPath=None):
    """文本文件轉換爲Excel文件
    :param txtPath: 文本文件即 .txt 文件的絕對路徑,e.g. txtPath = r"D:\PyExcel\test.txt";
    :param excelPath: 生成的Excel文件的絕對路徑,,e.g. excelPath = r"D:\PyExcel\Mytest.csv";
                      excelPath=None,表示不特意指定Excel的文件名稱,系統採用txt文件名稱命名Excel文件。
    """
    
    import win32com.client
    import os

    # xlApp = win32com.client.gencache.EnsureDispatch("Excel.Application")
    xlApp = win32com.client.Dispatch("Excel.Application")
    xlApp.DisplayAlerts = False  # 關閉警告
    xlApp.Visible = False   # Excel invisible 程序不可見

    xlBook = xlApp.Workbooks.Add()  # 新建工作簿
    sht = xlBook.Worksheets("Sheet1")    # 獲取工作表
    
    row = 0
    with open(txtPath, "r") as ftxt:
        lines = ftxt.readlines()
        for line in lines:
            line = line.strip().split()  # 刪除單行數據間的空格及尾部的換行符號
            row += 1
            col = 0
            for j in range(len(line)):
                col += 1
                sht.Cells(row, col).Value = line[j]
                # try: sht.Cells(row, col).Value = float(line[j])  # 字符串形式數值轉化爲浮點數.

    if excelPath is None:
        temp = os.path.splitext(txtPath)[0] # 不含拓展名的文件路徑+文件名稱
        ext = ".xlsx"
        excelPath = temp + ext
    else:
        ext = os.path.splitext(excelPath)[1]

    format = {".xlsx": 51, ".xls": 56, ".csv": 6}
    myFormat = format[ext] if ext in format else 51
    xlBook.SaveAs(excelPath, myFormat)

    xlBook.Close()
    xlApp.Quit()

    return print(f"""The file named "{excelPath}" has been created.""")


if __name__ == "__main__":
    # 測試代碼
    import time
    start = time.time()

    convertTxtToExcel(txtPath=r"D:\PyExcel\test.txt")
    # 特別注意:文件名不能寫成 "D:/PyExcel/test.txt",否則報錯。
    convertTxtToExcel(txtPath=r"D:\PyExcel\test.txt", excelPath=r"D:\PyExcel\Mytest.xlsx")
    convertTxtToExcel(txtPath=r"D:\PyExcel\test.txt", excelPath=r"D:\PyExcel\Mytest.xls")
    convertTxtToExcel(txtPath=r"D:\PyExcel\test.txt", excelPath=r"D:\PyExcel\Mytest.csv")

    end = time.time()
    dur = end - start
    dur = round(dur, 2)  # 保留2位小數
    print("Total time:", dur, "s.")  # print("It takes", dur, "s totally.")


九、尾聲


  以上,便是利用 pywin32 庫對 Excel 進行簡單二次開發的介紹。

  因篇幅有限,某些非關鍵功能未做詳細介紹,如有疑問,歡迎郵件來詢。

  僅以此文爲 Python 調用 Excel 有關功能的實現做個一個備忘,同時也爲有需要的人提供多一點參考。

  胸藏文墨懷若谷,腹有詩書氣自華,希望各位都能在知識的 pāo 子裏快樂徜徉。

  因個人水平有限,文中難免有所疏漏,還請各位大神不吝批評指正。

  最後,祝各位攻城獅們,珍愛生命,保護髮際線!

  本文部分內容來自圖書 Python Programming on Win32Excel VBA 參考

  歡迎大家點贊、評論及轉載,轉載請註明出處!

  爲我打call,不如爲我打款!

在這裏插入圖片描述




十、參考文獻


[1]. Mark Hammond, Andy Robinson. Python Programming on Win32 Chapter 9 - Integration with Excel.

[2]. 熱心市民小磊. win32com模塊 https://blog.csdn.net/u013289615/article/details/89295957.

[3]. zhu2695. C# Excel ColorIndex 對應列表 https://blog.csdn.net/zhu2695/article/details/47444745.

[4]. Python-Excel 模塊對比. 清風Python.

[5]. Python讀寫Excel文件第三方庫彙總,你想要的都在這兒!. ChrisMinions.




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章