前提要景:
最近收到這么一個需求,excel表格里面我們只想要結果,不要把底表發出來,也就是把excel里面做好的數據粘貼在新的excel,並選擇性粘貼為數值,並且保留格式。完成后發郵件給相應的經理老板們。在經過一系列跌跌撞撞,磕磕碰碰的錯誤下,寫了個excel自動把特定的區域復制粘貼到新的excel的sheet中,並保留了數值和格式。Python操作excel的模塊有千千萬,本文只挑選了win32com.client來進行操作,如有其它模塊的操作,記得艾特我學習一下!
啟動excel
Python啟動excel的常規操作有兩種:
第一種:
os.system('taskkill /IM EXCEL.exe /F')
xlapp = Dispatch('Excel.Application')
第二種:
os.system('taskkill /IM EXCEL.exe /F')
xlapp = win32com.client.gencache.EnsureDispatch('Excel.Application')
有什么區別呢?我也清楚得不仔細,EnsureDispatch的啟動方式要求格式比較嚴格,方法必須首字母大寫,最主要的是這種啟動方式可以使用win32com.client里面的excelVBA常量constants,而普通Dispatch不可以完成,選擇性粘貼PasteSpecial必須要用到constants的常量,故本文使用EnsureDispatch的方式啟動excel。
然后使用Visible為true,表示工作簿可見,
xlapp.Visible = True
DisplayAlerts為False表示為關閉警告,比如在保存時候,提示我們已經有相同文件了,是否保存並覆蓋,為false表示為不提示警告並覆蓋此文件。
xlapp.DisplayAlerts = False
Win32com之工作簿工作表的操作
打開指定路徑wkb_path的excel文件,這里的wkb_path為全路徑
wkb = xlapp.Workbooks.Open(wkb_path)
新建一個工作簿:
wkb_new = xlapp.Workbooks.Add()
保存工作簿:
wkb.Save() #保存已有的工作簿
wkb_new.SaveAs(new_path) #保存新的工作簿到指定的new_path下
新建一個工作表:
wkb_new.Worksheets.Add().Name = sheet_name
關閉工作簿:
wkb.Close()
xlapp.Quit()
退出excel應用程序
Win32com之單元格的操作
這里first_range指定區域的第一個單元格
first_row = old_wkb_sheet.Range(first_range).Row #取得old_wkb_sheet表中單元格first_range的行
first_row = old_wkb_sheet.Range(first_range).Column #取得old_wkb_sheet表中單元格first_range的列
last_row = old_wkb_sheet.Range(first_range).End(-4121).Row #取得old_wkb_sheet表中單元格first_range的向下有數據區域的最大行
last_col = old_wkb_sheet.Range(first_range).End(-4161).Column #取得old_wkb_sheet表中單元格first_range的向右有數據區域的最大列
old_wkb_sheet.Range(current_range).Copy() # 復制old_wkb_sheet表中current_range區域的值
new_wkb_sheet.Range(current_range).Paste() # 粘貼到new_wkb_sheet表中current_range區域中,Paste為全粘貼,包括格式數值等等
****額外小延伸****
vba中的Paste有sheet的Paste和range的Paste,在進行跨表復制粘貼的時候,只能用sheet的Paste,而跨表粘貼,則需要先激活需要粘貼的工作表
old_wkb_sheet.Range(current_range).Copy()
new_wkb_sheet.Range('A1').Select()
new_wkb_sheet.Range(new_current_range).Paste
當然,在用Paste的時候,會把包含格式公式的數據一起復制過去,但是這並不是想要的結果,事實上,我只想要數值和格式,這時候就需要用到PasteSpecial選擇性粘貼了,
我們先了解下PasteSpecial有那些常量:
Paste xlPasteType常量,指定復制的具體內容。默認為全部復制。
- 全部 xlPasteAll
- 公式 xlPasteFormulas
- 數值 xlPasteValues
- 格式 xlPasteFormats
- 批注 xlPasteComments
- 驗證 xlPasteValidation
- 所有使用源主題的單元 xlPasteAllUsingSourceTheme
- 邊框除外 xlPasteAllExceptBorders
- 列寬 xlPasteColumnWidths
- 公式和數字格式 xlPasteFormulasAndNumberFormats
- 值和數字格式 xlPasteValuesAndNumberFormats
- 所有合並條件格式 xlPasteAllMergingConditionalFormats
Operation xlPasteSpecialOperation常量,指明粘貼時要進行的運算操作,即將復制的單元格中的數據與指定單元格區域中的值進行加減乘除運算。
- 無 xlPasteSpecialOperationNone
- 加 xlPasteSpecialOperationAdd
- 減 xlPasteSpecialOperationSubtract
- 乘 xlPasteSpecialOperationMultiply
- 除 xlPasteSpecialOperationDivide
而在python中要使用vba常量,則必須使用EnsureDispatch的啟動方式,使用常量則要導入
from win32com.client import constants
那么來了,使用PasteSpecial來進行選擇性粘貼,可以這樣操作:
old_wkb_sheet.Range(current_range).Copy()
new_wkb_sheet.Range('A1').Select()
new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteValues,Operation = constants.xlNone)
new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteFormats,Operation = constants.xlNone)
new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteColumnWidths,Operation = constants.xlNone)
這里我進行了三次粘貼,一次數值,一次格式,一次列寬,就可以完成只保留數值格式的操作,列寬只是為了讓它完美好看。
****小延申****
那么怎么通過復制的方式,復制一張工作表呢?
首先選擇要復制的工作表的全部數據進行復制:
wkb.Worksheets(sheetname).Cells.Copy()
激活新的工作表
wkb_new.Worksheets(sheetname).Select()
粘貼
wkb_new.Worksheets(sheetname).Paste()
*****************************************以下是完全的代碼***********************************
1 import os 2 import win32com 3 from win32com.client import Dispatch,constants 4 5 path = r"\\10.250.50.23\共享\qsl\數值" 6 path_a = r"\\10.250.50.23\共享\qsl\數值\大表_EM_V3.xlsx" 7 8 #橫坐標轉換為數字 9 def colname_to_num(colname): 10 if type(colname) is not str: 11 return colname 12 col = 0 13 power = 1 14 for i in range(len(colname)-1,-1,-1): 15 ch = colname[i] 16 col += (ord(ch)-ord('A')+1)*power 17 power *= 26 18 return col 19 20 #數字轉換為橫坐標 21 def column_to_name(colnum): 22 if type(colnum) is not int: 23 return colnum 24 str = '' 25 while(not(colnum//26 == 0 and colnum % 26 == 0)): 26 temp = 25 27 if(colnum % 26 == 0): 28 str += chr(temp+65) 29 else: 30 str += chr(colnum % 26 - 1 + 65) 31 colnum //= 26 32 return str[::-1] 33 34 def wkb_client(path,wkb_path,class_Collection,newwkb_name): 35 os.system('taskkill /IM EXCEL.exe /F') 36 xlapp = win32com.client.gencache.EnsureDispatch('Excel.Application') 37 #xlapp = Dispatch('Excel.Application') 38 xlapp.Visible = True 39 xlapp.DisplayAlerts = False # 關閉警告 40 wkb = xlapp.Workbooks.Open(wkb_path) 41 print('文件【{}】已打開!'.format(wkb_path)) 42 wkb_new = xlapp.Workbooks.Add() 43 new_path = path + '\\{}'.format(newwkb_name) 44 print(newwkb_name) 45 wkb_new.SaveAs(new_path) 46 wkb_new.Close(1) 47 wkb_new = xlapp.Workbooks.Open(new_path) 48 49 for key,vlaue in class_Collection.items(): 50 51 sheet_name = class_Collection[key]['sheetname'] 52 first_range = class_Collection[key]['數據區域首行首列'] 53 54 old_wkb_sheet = wkb.Worksheets(sheet_name) 55 wkb_new.Worksheets.Add().Name = sheet_name 56 new_wkb_sheet = wkb_new.Worksheets(sheet_name) 57 first_row = old_wkb_sheet.Range(first_range).Row 58 first_col = old_wkb_sheet.Range(first_range).Column 59 last_row = old_wkb_sheet.Range(first_range).End(-4121).Row 60 last_col = old_wkb_sheet.Range(first_range).End(-4161).Column 61 last_rane = column_to_name(last_col)+str(last_row) 62 current_range = first_range + ':'+last_rane 63 print ('當前復制單元格區域為:{}'.format(current_range)) 64 new_current_range = 'A1'+':'+column_to_name(last_col-first_col+1)+str((last_row-first_row+1)) 65 print(new_current_range) 66 old_wkb_sheet.Range(current_range).Copy() 67 new_wkb_sheet.Range('A1').Select() 68 new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteValues,Operation = constants.xlNone) 69 new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteFormats,Operation = constants.xlNone) 70 new_wkb_sheet.Range(new_current_range).PasteSpecial(Paste = constants.xlPasteColumnWidths,Operation = constants.xlNone) 71 wkb.Worksheets(class_Collection['sheet0']['sheetname']).Cells.Copy() 72 wkb_new.Worksheets(class_Collection['sheet0']['sheetname']).Select() 73 wkb_new.Worksheets(class_Collection['sheet0']['sheetname']).Paste() 74 wkb.Save() 75 wkb_new.Save() 76 wkb.Close(1) 77 wkb_new.Close(1) 78 xlapp.Quit() 79 print('#更新 成功:%s' % wkb_path) 80 pass 81 82 83 class_Collection = {'sheet1':{'sheetname':'業務經營大表_姓名 (日)', 84 '數據區域首行首列':'A17',}, 85 86 'sheet2':{'sheetname':'組織管理大表_姓名 (日)', 87 '數據區域首行首列':'A17',}, 88 89 'sheet3':{'sheetname':'用戶運營大表_姓名 (日)', 90 '數據區域首行首列':'A17',}, 91 92 'sheet4':{'sheetname':'業務經營大表_姓名', 93 '數據區域首行首列':'A17',}, 94 95 'sheet5':{'sheetname':'用戶運營大表_姓名', 96 '數據區域首行首列':'A17',}, 97 98 'sheet6':{'sheetname':'組織管理大表_姓名', 99 '數據區域首行首列':'A17',}, 100 101 'sheet0':{'sheetname':'定義說明', 102 '數據區域首行首列':'A1',} 103 } 104 105 wkb_client(path,path_a,class_Collection,'大表_EM_數值.xlsx')