目 錄
阿陽的 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 等等。因此,充分利用好該庫可進行很多軟件的二次開發。
採用 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 系列軟件之間交互的一項軟件組件技術。
1.2 幫助文檔
Excel 二次開發的有關信息可在如下兩個官方網站中查看:
- Microsoft: office 開發人員中心 Excel 。
- GitHub:MicrosoftDocs/VBA-Docs/excel/Concepts 。
1.3 枚舉
Excel 中很多功能的實現都離不開枚舉,如設置文本居中,選擇另存爲文件的格式等等,各種枚舉類型可在 Office 開發人員中心 / 枚舉 (Excel) 中查詢。
名稱 | 值 | 說明 |
---|---|---|
xlCenter | -4108 | 居中 |
名稱 | 值 | 說明 | 拓展名 |
---|---|---|---|
xlCSV | 6 | CSV | *.csv |
xlExcel8 | 56 | Excel 97-2003工作簿 | *.xls |
xlWorkbookDefault | 51 | 默認工作簿 | *.xlsx |
1.4 顏色
Excel 內部的顏色索引如下圖所示:
有些情況需要採用非內部定義的顏色索引指定顏色,如邊框顏色的指定。此時,則按如下方式設置顏色:Object.Color = 16711935,16711935 爲洋紅色的標識號,顏色標識號 color 由顏色的 RGB 值換算而來,具體換算規則如下:
例如:對於白色(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 代碼。
三、工作簿 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 新建
- 新建自定義名稱工作表
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” 處於當前活躍狀態。
- 新建自定義位置工作表
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 引用
- 按索引號引用工作表
索引號是基於工作表標籤在同一類型的工作表中的位置(按從左到右的方式計數)分配給工作表的序號。以下代碼激活活動工作簿中的第一張工作表。
indexNum = 1
sht = xlBook.Worksheets(indexNum).Activate()
# totalNum = xlBook.Worksheets.Count
# indexNum <= totalNum
值得注意的是工作表的索引號起 始於 1 而非編程中常見的起始於 0,即索引號是幾就引用第幾張工作表(按左起計)。另外,索引號只能爲 不大於 當前工作簿中工作表總數的正整數,也就是說 indexNum = -1 不能表示引用最後一張工作表。
- 通過名稱引用工作表
mySheetName = "Sheet4"
sht = xlBook.Worksheets(mySheetName).Activate()
五、單元格 Cell
xlBook.Worksheets("Sheet1").Activate()
sht = xlBook.Worksheets("Sheet1")
5.1 引用
- 使用 A1 表示法引用單元格
按如下方式引用 “D4” 單元格:
cell1 = sht.Range("D4") # 引用 "D4" 單元格
- 索引號引用單元格
按如下方式引用第4行第2列的單元格:
row, col = 4, 2
cell2 = sht.Cells(row, col)
在 Excel 中,單元格的行號和列號也均從 從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 讀寫
- 寫入數據
cell1.Value = "from python"
cell2.Value = 1024
cell3.Value = "Hello Cell I5"
cell4.Value = "Hello Cell C2"
- 讀取數據
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)
- 字體
cell = sht.Range("C12")
cell.Value = "Font Test"
cell.Font.Name = "Times New Roman" # 字體名稱
- 加粗
cell = sht.Range("D12")
cell.Value = "Font Test"
cell.Font.Bold = True # 加粗
- 傾斜
cell = sht.Range("E12")
cell.Value = "Font Test"
cell.Font.Italic = True # 傾斜
- 顏色
cell = sht.Range("F12")
cell.Value = "Font Test"
cell.Font.ColorIndex = 3 # 紅色字體
5.5 格式
- 填充
cell = sht.Range("G12")
cell.Value = "Font Test"
cell.Interior.ColorIndex = 5 # 藍色背景色
- 對齊
cell = sht.Range("H12")
cell.Value = "Font Test"
cell.HorizontalAlignment = -4108 # 水平居中 XlHAlign 枚舉 指定對象的水平對齊方式
- 邊框
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 Win32 及 Excel 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.