前提要景:
最近收到这么一个需求,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')