经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
Python导出Excel图表
来源:cnblogs  作者:维尔布里兹  时间:2018/12/19 9:09:51  对本文有异议

Python自动化办公的过程,部分涉及到导出Excel图表;本篇主要讲下使用python代码将excel中的图表导出为图片的开发过程;

Python  版本:

  1. C:\Users>python
  2. Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
  3. Type "help", "copyright", "credits" or "license" for more information.>>>

Office版本:

数据准备

在导出图表前,先准备仿真数据并绘制图表,这里模仿运维工作的业务指标数据测试:

图表已经在Excel中绘制:

python导出Excel图表类

前期准备就绪,网上已有类似的导出Excel图表类,但是在后面的使用中发现问题,即关键函数已在下面代码中标红:

  1.  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 FileSystem10     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  #获取所有book20         self.SheetName = ''     #sheet名称21         self.ChartName = ''     #导出单张图表时,指定图表名称22         self.GetAllWorkbookCharts = False23         self.GetAllWorksheetCharts = True24         self.ExportPath = ''    #导出的文件路径25         self.ImageFilename = '' #导出的图片名称26         self.ReplaceWhiteSpaceChar = '_'27         self.ImageType = 'jpg'  #定义导出的图片类型28     def __del__(self):29         pass30     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会强行关闭正在打开的excel38         #可以使用 DispatchEx为单独调用线程,不影响已经打开的excel39         excel = Dispatch("excel.application")40         excel.Visible = False41         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             return53         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.ReplaceWhiteSpaceChar90         If self.ReplaceWhiteSpaceChar is an empty string then self.WorkBookFileName is left as is91         """92         if self.ReplaceWhiteSpaceChar != '':93             chartname.replace(' ',self.ReplaceWhiteSpaceChar)94         if self.ImageFilename == '': #未指定导出的图片名称,则与图表名称一致95             return chartname + "." + self.ImageType96         else: #指定了导出图片的命名格式97             return self.ImageFilename + "_" + chartname + "." + self.ImageType

 调用代码:

  1. docPath = r ===== =    Ect.ExportPath = r  Ect.start_export()
  2.    print("All Chart is Export!")

执行成功,接下来到上面设置的导出路径查看导出的图片:

  1.  E:\temp\Export_Img 的目录2018-12-18  11:20                 0 Chart 1.jpg2018-12-18  11:20                 0 Chart 2.jpg2018-12-18  11:20            39,583 Chart 3.jpg2018-12-18  11:20            38,950 Chart 4.jpg               4 个文件         78,533 字节
  2. E:\temp\Export_Img>

  从文件查看中看到,图表文件已经成功导出;

图表导出的问题

  但是,图表的导出并未能完全成功,从以上文件信息中看到导出的图片存在0字节的文件;点击查看图片可发现提示为空文件

   具体原因分析:

  经过本人多次的测试和探索发现:有效的图片为Excel的图表区域显示页面,通俗一点的说,即打开excel的图表所在sheet,当前屏幕显示了哪些图表,导出的图片就正常;在我个人认为可能是Office或Python对Excel的某种缓存功能,实际的缓存范围大概在当前显示页面的150%左右,超出区域的图表在未加载的情况下,导出成了0字节错误文件;

   即使发现了这个BUG,网上搜索也未能找到有效的类似"关闭加载缓存"的技术贴,那么还得根据导出图表的基础逻辑解决;

  继续测试,在Excel的图表中缩放显示全部图片测试,按照测试数据图表范围,缩放25%可显示全部图表(>_>或者把所有图表拖动到一个页面显示):

  1.  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.jpg2018-12-18  12:11             5,595 Chart 2.jpg2018-12-18  12:11             5,764 Chart 3.jpg2018-12-18  12:11             5,888 Chart 4.jpg               4 个文件         22,594 字节

  如上述文件查看所示,当图表所在的sheet页面显示了所有图表时,所有图表的图片都成功的导出;

  但是,缩放导出的图片是根据Excel的图标实际显示大小来导出的,所以缩放模式下,导出的图片大小、清晰度都不能正常使用;

解决方案

  综上所述,已知Python根据Excel的图标实际显示来导出,那么,可以让Python的导出代码执行前加载所有正常图表,在之前的python导出Excel图表的类中,使用异步方式调用excel.application,即文档以后台方式导出图表;

  如果需要完成Excel的所有图表加载,即必须手动或代码干预导出过程,在类中已经有代码可以设置文档可见;

  1. excel.Visible = True    #设置导出Excel是否可见,当值为True时,可见打开的Excel

  修改原代码:

  1. def _export(self):
  2.         excel = Dispatch("excel.application")# 启用独立的进程调用excelDispatch会强行关闭正在打开的excel# 可以使用 DispatchEx为单独调用线程,不影响已经打开的excelexcel.Visible = True
  3.         wb = excel.Workbooks.Open(os.path.join(self.WorkbookDirectory, self.WorkbookFilename))  #如需干预Excel图表导出,需要在文档打开到开始导出之间,加载完所有图表        self._get_Charts_In_Worksheet(wb, self.SheetName, self.ChartName)
  4.         wb.Close(False)
  5.         excel.Quit()

  解决方式显而易见,过程不多做描述;个人是使用win32api、win32con模块模拟键盘操作加载所有图表,因无法确认图表sheet所在的位置,需提前将图表所在的sheet设置在Excel文档的最后;或者可根据实际情况,由代码完成所有sheet的加载操作(比如多按几下pagadown翻页,屏幕识别判定内容范围等....)

   完整代码如下:

  1.   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.ReplaceWhiteSpaceChar115         If self.ReplaceWhiteSpaceChar is an empty string then self.WorkBookFileName is left as is116         """117         if self.ReplaceWhiteSpaceChar != '':118             chartname.replace(' ',self.ReplaceWhiteSpaceChar)119         if self.ImageFilename == '': #未指定导出的图片名称,则与图表名称一致120             return chartname + "." + self.ImageType121         else: #指定了导出图片的命名格式122             return self.ImageFilename + "_" + chartname + "." + self.ImageType123 124 125 docPath = r'E:\temp'126 if __name__=='__main__':127     Ect = Pyxlchart()128     Ect.WorkbookDirectory = docPath129     Ect.WorkbookFilename = 'Test.xlsx'130     Ect.SheetName = "图表"   #图表所在的sheet名称131     Ect.ExportPath = r'E:\temp\Export_Img'  #图片的导出路径132     Ect.start_export()

View Code

  执行,再次查看执行结果;

  1.  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.jpg2018-12-18  12:39            41,048 Chart 2.jpg2018-12-18  12:39            45,048 Chart 3.jpg2018-12-18  12:39            44,672 Chart 4.jpg               4 个文件        171,417 字节

  如上所示,文件的字节大小明显较比缩放导出模式大;到文件目录中双击图片查看,本次导出的图片大小、清晰度均正常

  总结

   从python导出Excel的图表来说,这一块的功能比较适用用单个图表的导出操作,如果涉及到大量的批量的图表导出,这种导出方式不太友好;实际工作如果涉及到批量的简单图表制作,重复度较高的工作性质可以由 matplotlib 模块自己绘制图表;

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号