pywin32+excel(一)——Python使用win32com/pywin32操作excel
文章目錄
- 1. 正式使用
- 1.0 代碼demo
- 1.1關于offset的說明
- 1.2 range使用
- 1.2.1 合并單元格的搜索問題
- 1.2.2 獲取Range對象范圍
- 1.3 小trick
- 1.3.1 兩個list構成一個dict
- 1.3.2 不使用多余的庫實現 字符串中 數字和字母分開
- 1.4 win32com.client.gencache.EnsureDispatch和Dispatch以及DispatchEx的區別
- 1.5 win32關閉當前程序操作的excel時也關閉了其他的excel窗口
- 2.wps和office沖突的問題
- 2.1 問題1
- 2.1.1 問題1.1
- 2.1.1 問題1.2
- 2.2 問題2 不用管
- 2.3查看當前調用對象的名字和版本
- 3. 部署時候的問題
- 福昕閱讀器com口沖突(`服務器沒有響應`錯誤)
- win32com相關的使用document
1. 正式使用
一千個讀者一千個哈姆雷特,單純 save()表 close()工作薄 還有quit()Excel APP,這三個寫法就有很多不同的解釋。。。
不喜歡解釋太多,直接看代碼吧,給一個簡單的讀取某個excel文件 修改后保存的例子。
1.0 代碼demo
excel = win32.Dispatch('Excel.Application') excel.Visible = False # 如果是True 會打開excel程序(界面) excel.DisplayAlerts = 0 # 不顯示警告信息 wb = excel.Workbooks.open('./xxx.xls') # 打開一個excel文件 最好使用絕對路徑 # office的函數調用時候不是特別在意大小寫(有些函數大小寫錯了照樣可以運行,有些就不行。。) wps有嚴格的大小寫限制 ws = wb.Worksheets('Sheet1') # 具體工作簿里的哪張工作表 一般默認Sheet1 操作都是對工作表操作的c_column = [15, 16, 18, 41] # 需要寫入的數據 xing = 10 # 某一行,這里指第十行 ws.Cells(11, 5).Value = 2 # Cells(row,col) 先行后列 Cells(11,5)就指的是 E11單元格 ws.Cells(11, 5).offset(3,2).Value =1 # E11 偏移后 到了 F13 ws.Range('D' + str(xing)).value = c_column[0] # 這里指對D10寫入數據 15 ws.Range('E' + str(xing)).value = c_column[1] # 這里指對E10寫入數據 16 ws.Range('F' + str(xing)).value = c_column[2] # 這里指對F10寫入數據 18 ws.Range('G' + str(xing)).value = c_column[3] # 這里指對G10寫入數據 41 wb.save # 保存表格(將修改保存到當前正在修改的表格) 保存當前工作簿 wb.SaveAs('xxx.xls') # 另存為為另一個表格 路徑也要是絕對路徑(如果不想修改原表格 可以注銷掉上一句 另存為即可) 將工作簿另存為 wb.Close(False) # True就是關閉該文件,并保存。不保存就是False 關閉工作簿 excel.Quit() # 關閉excel操作環境。1.1關于offset的說明
參考Excel VBA中Offset函數的用法和自己實測
- Offset函數實為定位函數。首先要找到中心點,如果以單元格A1為中心點,則A1的位置就是Offset(0,0)。
- 上圖以A1為中心,Offset兩個參數分別是行和列。以0為起點。
- 同理,E11 (11,5)偏移(3,2)后 (11+3-1,5+2-1)得到(13,6)即 F13
- E11偏移(1,0)到D11 (11,5)偏移(1,0) 即 (11+1-1,5+0-1)=(11,4)=11D 即 D11
- 所以offset(0,0)反倒會向左上移動一個。想要保持不變,應該是offset(1,1)。。。實測
- 上 左 是 負向 下 右 是正向
- 這一結論和網上搜到的VBA代碼結論不一致,可能是pywin32庫的問題
- 此外,注意到如果寫入單元格的內容很長,不會自動拉長單元格,需要自己打開excel之后手動縮放(也就是保持格式不變啦)
1.2 range使用
1.2.1 合并單元格的搜索問題
如果想定義Range(比如在A1-D10這個范圍內的單元格搜索“報賬單”),則需要使用Range對象,根據
Application.Range property (Excel),可知,定義Range的方式有兩種:
此外,在遇到合并單元格時,返回的Range對象的Address地址范圍只有左上角第一個的值,如果想要獲得該合并單元格所有的行/列,可以使用offset
比如上述工作表,如果Find(‘10V’) 使用range.Address返回的只是$D$38,同時,使用range.Areas.count返回的只是1,也就是說 這個合并單元格的起始位置是$D$38且只有1個(并不是返回基本單元格的數量)。為了方便后續操作,參考Find方法如何找到合并單元格的位置,針對這種問題有兩種常見解決方法
由于我面臨的場景不是單一合并(只是列方向或只是行方向),同時有許多合并單元格且合并的行數/列數并不一致,不能寫死,所以采用第二種方法,使用Range對象的MergeArea屬性(也是大多數情況下更方便推薦的一種)
range_freq = ws.Range(ws.Cells(1, 1), ws.Cells(10, 11)).Find('freq') range_range=range_freq.Find('range') print(range_range.Address,range_range.Areas.count) for i in range_range.MergeArea:print(i.Address)print(i.row,i.column) # 以數字形式代替字母打印出列號,行號 和 Cell(row,col)形式類似 // 此時打印出來就可以看到這個合并單元格里所有的基本單元格的(合并的行或者列都會打印出來) //如果某個合并單元格既有行合并又有列合并,則先按照行打印,再打印列上圖的excel表格區域打印后(先行后列):
$D$38 $E$38 $D$39 $E$39 $D$40 $E$40 $D$41 $E$41 $D$42 $E$42 $D$43 $E$43 $D$44 $E$44 $D$45 $E$45 $D$46 $E$46 $D$47 $E$471.2.2 獲取Range對象范圍
# 獲取10行-20行 1-9列之間的一個區域 freq_range = ws.Range(ws.Cells(10, 1),ws.Cells(20, 9)) freq_start_line = freq_range.Row # 區域起始行 等價于 freq_range.End(-4162).Row 有標題 freq_end_line = freq_range.End(-4121).Row # 區域的最后一行 但是注意 這里這個值一般需要-1 因為返回的是當前區域最后一行的下一行參考:MSDN——關于Range的End()
在Python中使用時,似乎不認識 xlUp 這些關鍵字,所以直接用對應的數值代替。Down對應最下面一行,ToLeft對應最左側一欄 同理,Up就是最上面一行 ToRight就是最右邊一列,四個數值就是Range的范圍了
1.3 小trick
1.3.1 兩個list構成一個dict
主要參考:Convert two lists into a dictionary
方法一是性能最高的方法,方法二類似列表構造器,但是成為字典構造器會更合適一點
//方法一 a=['1','2','3'] b=[1,2,3] c=dict(zip(a,b)) c Out[5]: {'1': 1, '2': 2, '3': 3} //方法二 new_dict = {k: v for k, v in zip(keys, values)}1.3.2 不使用多余的庫實現 字符串中 數字和字母分開
參考
Python 分割文本使得 字母和數字 分開?
但是最后發現還是re模塊用起來最簡單直接
1.4 win32com.client.gencache.EnsureDispatch和Dispatch以及DispatchEx的區別
在使用win32com調用excel的使用過程中,最常見的創建excel對象的方式有以下三種:
# 第一種 excel = win32.gencache.EnsureDispatch('Excel.Application') # 第二種 excel = win32com.client.Dispatch('Excel.Application') # 第三種 excel = win32com.client.DispatchEx('Excel.Application')參考
:DispatchEx會使用獨立進程
如下程序所示,
import win32com.client as win32 xl_dis = win32.Dispatch("Excel.Application") xl_dis Out[3]: <COMObject Excel.Application>import win32com.client as win32 xl_ens = win32.gencache.EnsureDispatch("Excel.Application") xl_ens Out[3]: <win32com.gen_py.Microsoft Excel 14.0 Object Library._Application instance at 0x35671240>Dispatch調用屬于late binding 調用時python不知道Excel object有哪些屬性和方法可以使用
而EnsureDispatch調用屬于earlybinding 調用時python知道Excel object有哪些屬性和方法可以使用,這是因為EnsureDispatch會先運行makeup.py文件, 反正找找自己的目錄,類似D:\anaconda\Lib\site-packages\win32com\client
此外,還可以在C:\Users\用戶名\AppData\Local\Temp\gen_py\3.7\00020813-0000-0000-C000-000000000046x0x1x8類似的目錄下找到這些內容,隨便打開一個,里面就是這個object的屬性方法,python也就是通過讀取這些文件才知道這個com對象有什么屬性方法的。
如果先使用了EnsureDispatch,那么就會產生這些文件,此時,不管調用Dispatch還是EnsureDispatch結果都一樣。但是如果刪除了這些文件,二者的不同之處就又會顯現,所以最大的不同就是一個會產這些輔助文件,一個不會。這些東西是寫在緩存文件夾位置的,不確定什么時候就會被清理
個人觀點:EnsureDispatch用在寫程序的時候,程序弄完之后,就用Dispatch比較好吧
使用EnsureDispatch的時候,有時候需要刪除緩存下的內容,重新產一遍
def open_excel():try:try:excel = win32.gencache.EnsureDispatch('Excel.Application')# print(excel.Name,excel.Version)except AttributeError:import shutilimport sys# Remove cache and try again.MODULE_LIST = [m.__name__ for m in sys.modules.values()]for module in MODULE_LIST:if re.match(r'win32com\.gen_py\..+', module):del sys.modules[module]shutil.rmtree(os.path.join(os.environ.get('LOCALAPPDATA'), 'Temp', 'gen_py'))excel = win32.gencache.EnsureDispatch('Excel.Application')excel.Visible = False # True 打開excel界面excel.DisplayAlerts = 0 # 不彈窗顯示警告信息 覆蓋同名文件時不彈出確認框(但是無法打開同名文件時會出錯 這個不屬于彈窗警告信息)except Exception as e:print('ERROR 02: excel打開錯誤:', e)else:return excel1.5 win32關閉當前程序操作的excel時也關閉了其他的excel窗口
參考Advanced Python and COM,可能是我沒有認真看,找不出相關的有效信息。
使用DispatchEx確實是新開了一個excel進程,關閉時候只關閉當前excel進程,而不會影響以及打開的其他excel進程。good
2.wps和office沖突的問題
2.1 問題1
Rebuilding cache of generated files for COM support has no attribute 'CLSIDToClassMap'搜索后,根據Issue in using win32com to access Excel file可知
// 如果想確保python啟動一個新的excel實例(例如,訪問xlsm文件中的宏),則使用 xlApp = win32com.client.DispatchEx("Excel.Application") //這樣,我可以關閉應用程序而不會損害已經打開的實例。否則,我可以簡單地使用 xlApp = win32com.client.Dispatch("Excel.Application")pywin32這里的坑挺多的
2.1.1 問題1.1
excel打開錯誤: module 'win32com.gen_py.45541000-5750-5300-4B49-4E47534F4655x0x3x0' has no attribute 'CLSIDToClassMap'參考:rdapaz/win32com.client.py,感謝這位大佬,大致就是把緩存清除,重新加載模塊中的一個文件
try:xl = client.gencache.EnsureDispatch('Excel.Application') except AttributeError:# Corner case dependencies.import osimport reimport sysimport shutil# Remove cache and try again.MODULE_LIST = [m.__name__ for m in sys.modules.values()]for module in MODULE_LIST:if re.match(r'win32com\.gen_py\..+', module):del sys.modules[module]shutil.rmtree(os.path.join(os.environ.get('LOCALAPPDATA'), 'Temp', 'gen_py'))from win32com import clientxl = client.gencache.EnsureDispatch('Excel.Application')2.1.1 問題1.2
應該才是真的核心問題,在問題1.1解決后報錯:
AttributeError: '<win32com.gen_py.Upgrade WPS Spreadsheets 3.0 Object Library (Beta).Workbooks instance at 0x1553056241672>' object has no attribute 'open'也就是說 我調用的明明是excel 但是卻使用了wps(大概就是端口沖突了),核心要解決的問題。
搜索發現:1. 安裝并卸載WPS后,導致office COM組件調用失敗的解決方案,大哥你真是個天使,我這個小白對端口和注冊表一看就頭大,愛你。
部分文章截取:
造成問題的原因是WPS惡意修改了office的com組件注冊信息,并且,不知道為什么,卸載和重新安裝都沒有修復 。WPS把office的接口全部指向自己,對應注冊表里 計算機\HKEY_CLASSES_ROOT\Interface{000208**-0000-0000-C0000-000000000046}\TypeLib 默認值改為了{45541000-5750-5300-4B49-4E47534F4655},原本應該為{00020813-0000-0000-C000-000000000046},Version改為了3.0,原本應該是1.7(office2010)。共有100+項,將以上修改正常就好了。附上修改后的注冊表文件。
- 先試試這個2. WPS Office 與 Microsoft Office 出現沖突的解決方法 操作后,重啟了Pycharm,報錯信息已經發生了變化
不用修改注冊表?直接使用wps配置工具取消兼容?(反正我把wps配置工具里的所有默認關聯打開文件全都取消了,哈哈哈),有興趣的可以自己去根據
1. 安裝并卸載WPS后,導致office COM組件調用失敗的解決方案這里面給的注冊表去看看,看是不是使用wps配置工具后 注冊表文件發生了改變。
反正是可以用了,解決,yeah!
2.2 問題2 不用管
pywintypes.com_error: (-2147417851, '服務器出現意外情況。', None, None)嘗試過讀寫excel數據出現服務器出現意外:我電腦上沒有福昕閱讀器或者其他閱讀器的插件,看過了,無效。
還有很多方法:
2.3查看當前調用對象的名字和版本
- Application.Version property (Excel)
Microsoft Excel 15.0(office2013對應的版本) 不是很區分大小寫(上面改成小寫的 excel.name 或者 excel.version都可以)
但是如果在office2016版本(Microsoft Excel 16.0)運行小寫形式的屬性name和version,則報錯
'<win32com.gen_py.Microsoft Excel 16.0 Object Library._Application instance at 0x3142152794952>' object has no attribute 'name'所以為了防止版本沖突(微軟office官方給的都是大寫),最好都改成大寫
3. 部署時候的問題
福昕閱讀器com口沖突(服務器沒有響應錯誤)
部署在其他人電腦的時候,報錯
服務器沒有響應 這個錯誤。
然后想起之前搜索相關錯誤的時候,出現過 excel中如果有福昕閱讀器插件的話,會占用excel的com口,所以試了一下,關閉了那人電腦上的福昕閱讀器的com加載項。 就ok了
win32com相關的使用document
中文:
- python win32com.client
- win32com 處理word和excel文檔說明
- python用win32com對Excel的一些操作
- win32com 處理word和excel文檔說明
- python3,用win32com操作excel及其宏
- Python Win32com模塊操作excel的幾個應用(一)
- Python-Win32com-Excel
英文:
- excelapp.py
- Automating Windows Applications Using COM
- Python Excel Mini Cookbook
- Python 2.7: Read and Write Excel file with win32com
- 下面這兩個網站其實屬于一個ip
- Python and com blowing the rest away
- PyWin32 Documentation
相關函數參考
- Application.Range property (Excel)
- Range object (Excel)
- Range.MergeArea property (Excel)
- Workbooks object (Excel)
- Worksheet object (Excel)
- Application.Cells property (Excel)
- Worksheet.SaveAs method (Excel)
- Workbooks.Close method (Excel)
- Workbooks.Open method (Excel)
- Application.Quit method (Excel)
- Workbook.Save method (Excel)
- Workbook.SaveAs method (Excel)
- win32.Dispatch vs win32.gencache in Python. What are the pros and cons?
- Python Programming on Win32-ebook-online
總結
以上是生活随笔為你收集整理的pywin32+excel(一)——Python使用win32com/pywin32操作excel的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Solarized ----vim配色方
- 下一篇: 捣鼓了一个月的SharePoint