Python导出Excel图表
Python自動化辦公的過程,部分涉及到導出Excel圖表;本篇主要講下使用python代碼將excel中的圖表導出為圖片的開發過程;
Python? 版本:
C:\Users>python Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>Office版本:
數據準備
在導出圖表前,先準備仿真數據并繪制圖表,這里模仿運維工作的業務指標數據測試:
圖表已經在Excel中繪制:
python導出Excel圖表類
前期準備就緒,網上已有類似的導出Excel圖表類,但是在后面的使用中發現問題,即關鍵函數已在下面代碼中標紅:
1 import win32com,os 2 from win32com.client import Dispatch 3 import pythoncom 4 ''' 5 啟用win32模塊導出excel的圖表,圖表需要打開加載緩存才能導出 6 ''' 7 class Pyxlchart(object): 8 """ 9 This class exports charts in an Excel Spreadsheet to the FileSystem 10 win32com libraries are required. 11 """ 12 def __init__(self): 13 ''' 14 初始化圖表 15 ''' 16 pythoncom.CoInitialize() #多線程使用win32com調用com組件的時初始化 17 self.WorkbookDirectory = '' #excel文件所在目錄 18 self.WorkbookFilename = '' #文件名稱 19 self.GetAllWorkbooks = False #獲取所有book 20 self.SheetName = '' #sheet名稱 21 self.ChartName = '' #導出單張圖表時,指定圖表名稱 22 self.GetAllWorkbookCharts = False 23 self.GetAllWorksheetCharts = True 24 self.ExportPath = '' #導出的文件路徑 25 self.ImageFilename = '' #導出的圖片名稱 26 self.ReplaceWhiteSpaceChar = '_' 27 self.ImageType = 'jpg' #定義導出的圖片類型 28 def __del__(self): 29 pass 30 def start_export(self,_visible=False): 31 if self.WorkbookDirectory == '': 32 return "WorkbookDirectory not set" 33 else: 34 self._export(_visible) 35 def _export(self,_visible=False): 36 excel = Dispatch("excel.application") 37 #啟用獨立的進程調用excel,Dispatch會強行關閉正在打開的excel 38 #可以使用 DispatchEx為單獨調用線程,不影響已經打開的excel 39 excel = Dispatch("excel.application") 40 excel.Visible = False 41 wb = excel.Workbooks.Open(os.path.join(self.WorkbookDirectory ,self.WorkbookFilename)) 42 self._get_Charts_In_Worksheet(wb,self.SheetName,self.ChartName) 43 wb.Close(False) 44 excel.Quit() 45 46 def _get_Charts_In_Worksheet(self,wb,worksheet = "", chartname = ""): 47 if worksheet != "" and chartname != "": 48 sht = self._change_sheet(wb,worksheet) 49 cht = sht.ChartObjects(chartname) 50 51 self._save_chart(cht) 52 return 53 if worksheet == "": #導出表格中所有圖表 54 for sht in wb.Worksheets: 55 for cht in sht.ChartObjects(): 56 if chartname == "": 57 self._save_chart(cht) 58 else: 59 if chartname == cht.Name: 60 self._save_chart(cht) 61 else: #導出指定sheet中的圖標 62 sht = wb.Worksheets(worksheet) 63 for cht in sht.ChartObjects(): 64 if chartname == "": 65 self._save_chart(cht) 66 else: 67 if chartname == cht.Name: 68 self._save_chart(cht) 69 def _change_sheet(self,wb,worksheet): 70 try: 71 return wb.Worksheets(worksheet) 72 except: 73 raise NameError('Unable to Select Sheet: ' + worksheet + ' in Workbook: ' + wb.Name) 74 def _save_chart(self,chartObject): 75 ''' 76 保存圖標到指定路徑 77 :param chartObject: 圖表名稱 78 :return: 79 ''' 80 imagename = self._get_filename(chartObject.Name) 81 savepath = os.path.join(self.ExportPath,imagename) 82 #print(savepath) 83 84 chartObject.Chart.Export(savepath,self.ImageType) 85 86 def _get_filename(self,chartname): 87 """ 88 獲取導出圖表的文件名稱 89 Replaces white space in self.WorkbookFileName with the value given in self.ReplaceWhiteSpaceChar 90 If self.ReplaceWhiteSpaceChar is an empty string then self.WorkBookFileName is left as is 91 """ 92 if self.ReplaceWhiteSpaceChar != '': 93 chartname.replace(' ',self.ReplaceWhiteSpaceChar) 94 if self.ImageFilename == '': #未指定導出的圖片名稱,則與圖表名稱一致 95 return chartname + "." + self.ImageType 96 else: #指定了導出圖片的命名格式 97 return self.ImageFilename + "_" + chartname + "." + self.ImageType?調用代碼:
docPath = r'E:\temp' if __name__=='__main__':Ect = Pyxlchart()Ect.WorkbookDirectory = docPathEct.WorkbookFilename = 'Test.xlsx'Ect.SheetName = "圖表" #圖表所在的sheet名稱Ect.ExportPath = r'E:\temp\Export_Img' #圖片的導出路徑Ect.start_export()print("All Chart is Export!")
執行成功,接下來到上面設置的導出路徑查看導出的圖片:
E:\temp\Export_Img 的目錄 2018-12-18 11:20 0 Chart 1.jpg 2018-12-18 11:20 0 Chart 2.jpg 2018-12-18 11:20 39,583 Chart 3.jpg 2018-12-18 11:20 38,950 Chart 4.jpg4 個文件 78,533 字節 E:\temp\Export_Img>從文件查看中看到,圖表文件已經成功導出;
圖表導出的問題
但是,圖表的導出并未能完全成功,從以上文件信息中看到導出的圖片存在0字節的文件;點擊查看圖片可發現提示為空文件
? 具體原因分析:
經過本人多次的測試和探索發現:有效的圖片為Excel的圖表區域顯示頁面,通俗一點的說,即打開excel的圖表所在sheet,當前屏幕顯示了哪些圖表,導出的圖片就正常;在我個人認為可能是Office或Python對Excel的某種緩存功能,實際的緩存范圍大概在當前顯示頁面的150%左右,超出區域的圖表在未加載的情況下,導出成了0字節錯誤文件;
? 即使發現了這個BUG,網上搜索也未能找到有效的類似"關閉加載緩存"的技術貼,那么還得根據導出圖表的基礎邏輯解決;
繼續測試,在Excel的圖表中縮放顯示全部圖片測試,按照測試數據圖表范圍,縮放25%可顯示全部圖表(>_>或者把所有圖表拖動到一個頁面顯示):
E:\temp\Export_Img 的目錄 2018-12-18 11:20 <DIR> . 2018-12-18 11:20 <DIR> .. 2018-12-18 12:11 5,347 Chart 1.jpg 2018-12-18 12:11 5,595 Chart 2.jpg 2018-12-18 12:11 5,764 Chart 3.jpg 2018-12-18 12:11 5,888 Chart 4.jpg4 個文件 22,594 字節如上述文件查看所示,當圖表所在的sheet頁面顯示了所有圖表時,所有圖表的圖片都成功的導出;
但是,縮放導出的圖片是根據Excel的圖標實際顯示大小來導出的,所以縮放模式下,導出的圖片大小、清晰度都不能正常使用;
解決方案
綜上所述,已知Python根據Excel的圖標實際顯示來導出,那么,可以讓Python的導出代碼執行前加載所有正常圖表,在之前的python導出Excel圖表的類中,使用異步方式調用excel.application,即文檔以后臺方式導出圖表;
如果需要完成Excel的所有圖表加載,即必須手動或代碼干預導出過程,在類中已經有代碼可以設置文檔可見;
excel.Visible = True #設置導出Excel是否可見,當值為True時,可見打開的Excel修改原代碼:
def _export(self):excel = Dispatch("excel.application")# 啟用獨立的進程調用excel,Dispatch會強行關閉正在打開的excel# 可以使用 DispatchEx為單獨調用線程,不影響已經打開的excelexcel.Visible = Truewb = excel.Workbooks.Open(os.path.join(self.WorkbookDirectory, self.WorkbookFilename)) #如需干預Excel圖表導出,需要在文檔打開到開始導出之間,加載完所有圖表 self._get_Charts_In_Worksheet(wb, self.SheetName, self.ChartName)wb.Close(False)excel.Quit()解決方式顯而易見,過程不多做描述;個人是使用win32api、win32con模塊模擬鍵盤操作加載所有圖表,因無法確認圖表sheet所在的位置,需提前將圖表所在的sheet設置在Excel文檔的最后;或者可根據實際情況,由代碼完成所有sheet的加載操作(比如多按幾下pagadown翻頁,屏幕識別判定內容范圍等....)
? 完整代碼如下:
1 import os,time,sys 2 import win32api 3 import win32con 4 from win32com.client import Dispatch 5 import pythoncom 6 ''' 7 啟用win32模塊導出excel的圖表,圖表需要打開加載緩存才能導出 8 ''' 9 class Pyxlchart(object): 10 """ 11 This class exports charts in an Excel Spreadsheet to the FileSystem 12 win32com libraries are required. 13 """ 14 def __init__(self): 15 ''' 16 初始化圖表 17 ''' 18 pythoncom.CoInitialize() 19 self.WorkbookDirectory = '' #excel文件所在目錄 20 self.WorkbookFilename = '' #文件名稱 21 self.GetAllWorkbooks = False #獲取所有book 22 self.SheetName = '' #sheet名稱 23 self.ChartName = '' #導出單張圖表時,指定圖表名稱 24 self.GetAllWorkbookCharts = False 25 self.GetAllWorksheetCharts = True 26 self.ExportPath = '' #導出的文件路徑 27 self.ImageFilename = '' #導出的圖片名稱 28 self.ReplaceWhiteSpaceChar = '_' 29 self.ImageType = 'jpg' 30 def __del__(self): 31 pass 32 def start_export(self,_visible=True): 33 if self.WorkbookDirectory == '': 34 return "WorkbookDirectory not set" 35 else: 36 self._export(_visible) 37 def _export(self,_visible): 38 """ 39 Exports Charts as determined by the settings in class variabels. 40 """ 41 excel = Dispatch("excel.application") 42 #啟用獨立的進程調用excel,Dispatch容易沖突【會強行關閉正在打開的excel】 43 #使用 DispatchEx為單獨調用線程,不影響已經打開的excel 44 45 excel.Visible = _visible 46 wb = excel.Workbooks.Open(os.path.join(self.WorkbookDirectory ,self.WorkbookFilename)) 47 48 time.sleep(5) # 等5秒等待進程打開加載文檔 49 # 使用打開excel的方式,則模擬鍵盤事件觸發加載所有圖表 50 if excel.Visible == 1 or excel.Visible == True: 51 win32api.keybd_event(17, 0, 0, 0) # 鍵盤按下 ctrl鍵 52 time.sleep(1) 53 for k in range(4): 54 win32api.keybd_event(34, 0, 0, 0) # ctrl+pageDown的組合會跳轉sheet,20次跳轉可以到最后的圖表 55 win32api.keybd_event(36, 0, 0, 0) # 鍵盤按下 home鍵,和上個按鍵形成組合鍵,回到第一行開頭 56 win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0) 57 win32api.keybd_event(36, 0, win32con.KEYEVENTF_KEYUP, 0) 58 59 # 當表格過大時,只能保存到頁面顯示的圖標,故此需要先循環翻頁將所有圖片加載 60 for i in range(15): # 翻頁加載所有圖表 61 win32api.keybd_event(34, 0, 0, 0) # 每次讀取之后翻頁 62 win32api.keybd_event(34, 0, win32con.KEYEVENTF_KEYUP, 0) 63 time.sleep(0.5) 64 65 #圖片加載完成,好了,導出圖片繼續進行 66 self._get_Charts_In_Worksheet(wb,self.SheetName,self.ChartName) 67 wb.Close(True) 68 excel.Quit() 69 70 71 def _get_Charts_In_Worksheet(self,wb,worksheet = "", chartname = ""): 72 if worksheet != "" and chartname != "": 73 sht = self._change_sheet(wb,worksheet) 74 cht = sht.ChartObjects(chartname) 75 76 self._save_chart(cht) 77 return 78 if worksheet == "": #導出表格中所有圖表 79 for sht in wb.Worksheets: 80 for cht in sht.ChartObjects(): 81 if chartname == "": 82 self._save_chart(cht) 83 else: 84 if chartname == cht.Name: 85 self._save_chart(cht) 86 else: #導出指定sheet中的圖標 87 sht = wb.Worksheets(worksheet) 88 for cht in sht.ChartObjects(): 89 if chartname == "": 90 self._save_chart(cht) 91 else: 92 if chartname == cht.Name: 93 self._save_chart(cht) 94 def _change_sheet(self,wb,worksheet): 95 try: 96 return wb.Worksheets(worksheet) 97 except: 98 raise NameError('Unable to Select Sheet: ' + worksheet + ' in Workbook: ' + wb.Name) 99 def _save_chart(self,chartObject): 100 ''' 101 保存圖標到指定路徑 102 :param chartObject: 圖表名稱 103 :return: 104 ''' 105 imagename = self._get_filename(chartObject.Name) 106 savepath = os.path.join(self.ExportPath,imagename) 107 #print(savepath) 108 109 chartObject.Chart.Export(savepath,self.ImageType) 110 111 def _get_filename(self,chartname): 112 """ 113 獲取導出圖表的文件名稱 114 Replaces white space in self.WorkbookFileName with the value given in self.ReplaceWhiteSpaceChar 115 If self.ReplaceWhiteSpaceChar is an empty string then self.WorkBookFileName is left as is 116 """ 117 if self.ReplaceWhiteSpaceChar != '': 118 chartname.replace(' ',self.ReplaceWhiteSpaceChar) 119 if self.ImageFilename == '': #未指定導出的圖片名稱,則與圖表名稱一致 120 return chartname + "." + self.ImageType 121 else: #指定了導出圖片的命名格式 122 return self.ImageFilename + "_" + chartname + "." + self.ImageType 123 124 125 docPath = r'E:\temp' 126 if __name__=='__main__': 127 Ect = Pyxlchart() 128 Ect.WorkbookDirectory = docPath 129 Ect.WorkbookFilename = 'Test.xlsx' 130 Ect.SheetName = "圖表" #圖表所在的sheet名稱 131 Ect.ExportPath = r'E:\temp\Export_Img' #圖片的導出路徑 132 Ect.start_export() View Code執行,再次查看執行結果;
E:\temp\Export_Img 的目錄 2018-12-18 11:20 <DIR> . 2018-12-18 11:20 <DIR> .. 2018-12-18 12:39 40,649 Chart 1.jpg 2018-12-18 12:39 41,048 Chart 2.jpg 2018-12-18 12:39 45,048 Chart 3.jpg 2018-12-18 12:39 44,672 Chart 4.jpg4 個文件 171,417 字節如上所示,文件的字節大小明顯較比縮放導出模式大;到文件目錄中雙擊圖片查看,本次導出的圖片大小、清晰度均正常
總結
從python導出Excel的圖表來說,這一塊的功能比較適用用單個圖表的導出操作,如果涉及到大量的批量的圖表導出,這種導出方式不太友好;實際工作如果涉及到批量的簡單圖表制作,重復度較高的工作性質可以由 matplotlib 模塊自己繪制圖表;
?
轉載于:https://www.cnblogs.com/Wherebolezi/p/10135880.html
總結
以上是生活随笔為你收集整理的Python导出Excel图表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux基础命令---zip
- 下一篇: TF.js 识别图片中的物件