巧用python win32com模塊操作excel文件


  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

 

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)

該方法親測可用,特此奉上~~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM