Python操作excel文件的第三方庫有很多,小爬就常用openPyxl庫來操作已有的excel文件,它對xlsx、xlsm等格式的支持都較好。可openPyxl也有不足,它難以實習VBA中的很多功能。如果我們平日里對VBA語法很熟悉,則可以通過win32com.client來操縱excel文件,語法非常類似。
之所以不直接使用VBA,是因為VBA擅長跟excel打交道,不擅長跟外部應用打交道。小爬最近就遇到這樣一個自動化場景:先利用python爬蟲的方法,獲取到服務器端的多個excel文件,然后對這些excel文件進行跨表操作,單純的VBA實現起來比較繁瑣,用python和VBA各實現一部分不利於腳本的封裝,割裂感較強。
話不多說,我們看看如何用win32com來控制excel,首先我們需要用pip安裝pywin32庫,之后就可以使用了:
import win32com.client
下面的代碼演示了一些常規的語法操作,與vba如出一轍,只是需要代碼pythonic:
import win32com.client import os base_dir=os.path.dirname(os.path.abspath(__file__)) # 獲取當前路徑 xlApp = win32com.client.Dispatch('Excel.Application') xlApp.Visible=1 # 顯示excel界面 filename="test.xlsx" fullPath=os.path.join(base_dir,filename) # 得到完整的filepath xlBook = xlApp.Workbooks.Open(fullPath, ReadOnly = False) #打開對飲的excel文件 sht = xlBook.Worksheets('Sheet1') # 打開對應名稱的sheet sht.UsedRange.ClearContents() # 對當前使用區域清除內容 nrows=sht.UsedRange.Rows.Count # 獲取使用區域的行數 sht.UsedRange.Copy() #復制 sht.Activate() # 激活當前工作表
光這些還不夠,比如我們希望實現excel的復制&粘貼值操作,vba的語法類似這樣:
Sub 宏1() ' Range("A1").Select Selection.CurrentRegion.Select Selection.Copy Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False End Sub
這段VB風格的代碼如何pythonic且不出錯呢,你可能首先會想這樣改造:
Range("A1").Select() Selection.CurrentRegion.Select() Selection.Copy() Selection.PasteSpecial(Paste=xlPasteValues, Operation=xlNone, SkipBlanks=False, Transpose=False)
VBA中默認你操作的當前worksheet,所以可以直接使用Range對象,Selection丟向,但是python中不能直接這樣簡寫,改造和簡化后應該是:
sht.Range("A1").CurrentRegion.Copy() sht.Range("A1").CurrentRegion.PasteSpecial(Paste=xlPasteValues, Operation=xlNone, SkipBlanks=False, Transpose=False)
其實這樣還是會報錯,因為python並無法知道xlPasteValues、xlNone這些常量到底為多少,因為我們沒有提前定義它。如何查詢這些常量實際的值,一個簡單的方法還是通過VBA的對象瀏覽器,打開excel,按住快捷鍵Alt + F11進入VBE界面,見下圖:



有了這些,上面的偽代碼就可以改成如下形式,成功運行通過:
sht.cells(1,1).PasteSpecial(Paste=-4163, Operation=-4142, SkipBlanks=False, Transpose=False)
小爬知道了這些后,想用分列功能將excel某列文本型數值轉為常規的數值格式,操作如動態所示,

vba代碼示例如下:
Columns("A:A").Select Selection.TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _ TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _ Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _ :=Array(1, 1), TrailingMinusNumbers:=True
我們故技重施,可以查到xlDelimited、xlDoubleQuote 這些常量的值,但是這個Array(1, 1) 怎么轉化為python語法,小爬目前還沒找到合適的方法,有知道的童鞋,可以留言區告訴我,謝謝~
上面的例子說明,上文提到的方法有一定局限性。其實我們可以利用選擇性粘貼(乘以1)來達到同樣的效果:文本型數字轉常規數值,演示動圖如下:

這段方法,需要借助一個輔助單元格,將其賦值1,待操作完畢后,再清空該輔助單元格的值即可,這個用VBA代碼示例如下:
Range("B2").Select ActiveCell.FormulaR1C1 = "1" Range("B2").Select Selection.Copy Range("A2:A20").Select Selection.PasteSpecial Paste:=xlPasteAll, Operation:=xlMultiply, _ SkipBlanks:=False, Transpose:=False
這次,我們可以用上面的方法得到xlPasteAll、xlMultiply等常量的值,將上面的代碼python化即可;
利用這個方法,我們還可以很輕易得到某一列的最后一行的行號,比如A列的,可以這樣寫:
max_rowA=sht.cells(sht.Rows.Count,1).End(-4162).Row
這回,你應該能看出來上面代碼中的-4162 是怎么來的了。
希望上面列舉的示例,能給各位希望用python win32com.client來操作excel文件的童鞋,一點點借鑒和提示!還不趕緊動手試試?
=============================================================================================
(2020-08-27日更新)
再次查閱了微軟的官方文檔后,找到關於數據分列(TextToColumns)實現文本型數值轉為常規數值的解決方法,見網址:https://docs.microsoft.com/en-us/office/vba/api/excel.range.texttocolumns
詳細內容如下:
Parameters
| Name | Required/Optional | Data type | Description |
|---|---|---|---|
| Destination | Optional | Variant | A Range object that specifies where Microsoft Excel will place the results. If the range is larger than a single cell, the top left cell is used. |
| DataType | Optional | XlTextParsingType | The format of the text to be split into columns. |
| TextQualifier | Optional | XlTextQualifier | Specifies whether to use single, double, or no quotes as the text qualifier. |
| ConsecutiveDelimiter | Optional | Variant | True to have Excel consider consecutive delimiters as one delimiter. The default value is False. |
| Tab | Optional | Variant | True to have DataType be xlDelimited and to have the tab character be a delimiter. The default value is False. |
| Semicolon | Optional | Variant | True to have DataType be xlDelimited and to have the semicolon be a delimiter. The default value is False. |
| Comma | Optional | Variant | True to have DataType be xlDelimited and to have the comma be a delimiter. The default value is False. |
| Space | Optional | Variant | True to have DataType be xlDelimited and to have the space character be a delimiter. The default value is False. |
| Other | Optional | Variant | True to have DataType be xlDelimited and to have the character specified by the OtherChar argument be a delimiter. The default value is False. |
| OtherChar | Optional | Variant | Required if Other is True; the delimiter character when Other is True. If more than one character is specified, only the first character of the string is used; the remaining characters are ignored. |
| FieldInfo | Optional | Variant | An array containing parse information for the individual columns of data. The interpretation depends on the value of DataType. When the data is delimited, this argument is an array of two-element arrays, with each two-element array specifying the conversion options for a particular column. The first element is the column number (1-based), and the second element is one of the XlColumnDataType constants specifying how the column is parsed. |
| DecimalSeparator | Optional | Variant | The decimal separator that Microsoft Excel uses when recognizing numbers. The default setting is the system setting. |
| ThousandsSeparator | Optional | Variant | The thousands separator that Excel uses when recognizing numbers. The default setting is the system setting. |
| TrailingMinusNumbers | Optional | Variant | Numbers that begin with a minus character. |
可以看到 關於excel錄屏得到的VBA分列的代碼中,“FieldInfo :=Array(1, 1)”,FieldInfo 字段是可選的,當我們並不准備把數據分成幾列的時候,可以不需要該字段。比如我想要對F列進行分列,將文本型數字轉為數字,pythonic(win32com.client)的代碼是這樣的,親測可用:
workNumSht.Columns("F:F").TextToColumns(Destination=workNumSht.Range("F1"), DataType=2, TrailingMinusNumbers=True)
=============================================================================================
(2020-12-21日更新)
上文的方法中,當時小爬還沒能找到VBA中的Array類型如何利用python win32com模塊來表達,現已找到解決辦法。我們知道在VBA中,如下代碼就可以輕松將某一行值寫入一個一維數組:
dim arr() as string arr=sheet.Range("A1:M1").Value
我們就可以假定vba中的sheet.Range("A1:M1").Value 屬性可以得到一個類似於Array(1,1)的數組對象。我們可以這樣去驗證這個vba數組對象在python win32com中是如何實現的。我試着利用win32com模塊來 print(type(sht.Range("A1:B1").Value)),結果系統返回了一個Tuple元組類型。
這個時候,我們VBA中錄制得到的分列功能(TextToColumns)就可以隨意python化了。比如下圖中,我們希望將B列的字母和數字分開成兩列:

VBA錄制宏得到這段分列的代碼如下:
Sub 宏1() ' ' Columns("B:B").Select Selection.TextToColumns Destination:=Range("B1"), DataType:=xlDelimited, _ TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _ Semicolon:=False, Comma:=False, Space:=False, Other:=True, OtherChar _ :="_", FieldInfo:=Array(Array(1, 1), Array(2, 1)), TrailingMinusNumbers:=True End Sub
我們在python win32com.client中可以這樣表達:
xlDelimited=1 xlDoubleQuote=1 sht.Columns("B:B").TextToColumns(Destination=sht.Range("B1"), DataType=xlDelimited, TextQualifier=xlDoubleQuote, ConsecutiveDelimiter=False, Tab=False, Semicolon=False, Comma=False, Space=False, Other=True, OtherChar ="_", FieldInfo=((1, 1),(2,1)), TrailingMinusNumbers=True)
該方法親測可用,特此奉上~~~
