Python自动化之操作PPT--更新模板数据,减少重复操作

Python自动化之操作PPT

日常工作中会遇见很多重复性的操作,利用Python可以让我们从繁忙的工作中解脱出来。本次要分析的就是运用Python操作PPT以实现自动化生产PPT文件。

需求分析

工作中一同事需要结合Excel数据来制作PPT报告,以便甲方更好的看到商品部分指标的变化趋势。目前同事已经做好了一个模板文件,需要完成将它运用到其他数据上。
PPT样式如下:

在这里插入图片描述

问题分析以及思路分享

原需求是想运用Python来生成PPT文件,于是需要了解到Python操作PPT的库,根据这个库来生成想要的PPT文件。

第一次尝试

由于第一次接触到Python操作PPT,在网上得知,Python操作PPT的库为python-pptx。于是安装python-pptx.

pip install python-pptx

安装完之后就可以对pptx进行操作了。
由于第一次使用有些操作不是很熟,阅读了网上的一些案例,结合官方文档 便开始一顿操作了。
可当正要操作是便发现,网上的大多数教程都是如何创建一页简单的PPT,讲到了Presentation,占位符等对象,可如上图所示,我操作的是生成一个图表,还有一个表格,当PPT播放时,折线图还有一个动态的过程。于是我陷入了沉思,这个轮子我可造不了,对于python-pptx我还不是很了解,这可不能快速的完成自动化的任务。

第二次尝试

虽然我不能创建PPT,但是目前已经有一个做好的模板PPT,借助于前段时间做过的生成报告证书的经历,报告证书是其中大多数数据不变,变化商品名称和日期然后输出pdf文件,在模板文件中运用占位符就可以解决。于是顺着这个思路,我能否通过读取PPT模板,修改其中的部分数据,而不改变结构,那么问题也就解决了。
于是运用python-pptx读取pptx文件,获取该对象,对该对象进行操作。
此时不得不打开官方文档了解其属性方法,一遍对其更好的操作。
慢,这样一来不又是进入了造轮子的阶段吗,我并不需要对pptx库进行全面的了解,核心是对数据进行替换。于是我只需要知道原PPT中一个数据对应的属性方法是什么就可以了(有点像爬虫),通过值找到对应的标签。下面正式开始。

读取pptx文件,读取一些基本知道该对象的属性方法

from pptx import Presentation
prs = Presentation("123.pptx")
slides = prs.slides
number_pages = len(slides)
page = slides[0]
print(dir(page.shapes))

有上面输出可以得到 每个PPT页面的对象都为一个slider,每页中出现的内容都在page.shape中。
可以发现page.shapes有如下属性:
‘adjustments’, ‘auto_shape_type’, ‘click_action’, ‘element’, ‘fill’, ‘get_or_add_ln’, ‘has_chart’, ‘has_table’, ‘has_text_frame’, ‘height’, ‘is_placeholder’, ‘left’, ‘line’, ‘ln’, ‘name’, ‘part’, ‘placeholder_format’, ‘rotation’, ‘shadow’, ‘shape_id’, ‘shape_type’, ‘text’, ‘text_frame’, ‘top’, ‘width’。
结合对pptx的初步了解,
text是和文字相关的,table是和表格相关的,chart是和图表相关的,其他的有一些边框大小,填充字体等属性。于是接着尝试

for shape in page.shapes:
    # print(dir(shape))
    if shape.has_chart:
        print("ok")
    if shape.has_tacle:
        print("ok")

果然,返回的结果都是ok,也就是刚好和原来PPT中的折线图以及表格相对应。(感觉发现了新世纪的大门)

接着就是分别对chart和table进行操作了。(由于走了很多坑这里的顺序和实际完成的顺序不太一样)

对table进行操作

table做为一个表格对象,从原图就可以看出,table有如下几种属性:变宽间距,字体颜色,字体形式,字体大小,对齐方式。刚好这里在table对象都有了很好的体现。而我要做的就是如何修改这些达到要求。这其中对于字体颜色,有些原pptx的颜色在python-pptx中并读不出来,这里的小技巧就是通过取色器获取原ppt文件中字体的RGB颜色,然后用python创建这样的一个颜色对象,来填充之前的属性。

for shape in page.shapes:
    if shape.has_table:
        table = shape.table
        # 获取table的一个格,类似openpyxl
        ii = 2
        j = 1
        cell = table.cell(ii, j + 1)
        # 填充的数据
        da = get_da(ci, j + 1)
        pr = cell.text_frame.paragraphs[0].runs[0]
        pr.text = da
        
        # 字体大小
        pr.font.size = Pt(11)
        if da == "-" or da == "0%":
            # 颜色
            pr.font.color.rgb = RGBColor(0, 0, 0)
        elif "-" in da:
            pr.font.color.rgb = RGBColor(156, 0, 6)
        else:
            pr.font.color.rgb = RGBColor(84, 130, 53)
        # 字体
        pr.font.name = "Arial"

大功告成,table的给中属性和实际使用吻合很高。

对chart进行操作

在对chart进行操作时,可以得到chart本身的属性。于是直接查看chart的属性和方法:

for shape in page.shapes:
    if shape.has_chart:
        chart = shape.chart
        print(dir(chart))
        chart_data = ChartData(chart)
        cart = chart_data.categories
        for s in cart:
            print(s)
            print(s.label)
        for s in chart.series:
            print(s.data_labels.show_value)
            print(s.name)

可以找到chart有如下属性:
‘category_axis’, ‘chart_style’, ‘chart_title’, ‘chart_type’, ‘element’, ‘font’, ‘has_legend’, ‘has_title’, ‘legend’, ‘part’, ‘plots’, ‘replace_data’, ‘series’, ‘value_axis’
这些和Python中的图都很相似,于是我尝试获取到了图表中的值也惊奇的发现,视乎离成功更近一步了。下一步就是想如何改变这些值成新的值了。于是我发现了replace_data方法,是否运用replace方法就可以完成操作了,于是我查找了相关replace的使用方式,通过不断努力发现数据可以该但是并不是我想要的样子。没有了之前的动态显示过程,操作过程也比较复杂,当我在ppt编辑数据时,原数据居然没有变化,我深知这事还没有结束。于是我去查找API发现其中这样一个描述

class pptx.chart.series.AreaSeries
	A data point series belonging to an area plot.
	...
	values
	Read-only. A sequence containing the float values for this series, in the order they appear on the chart.

也就是说我获取到的图表区,部分参数是只读的,仔细一想图表是原数据的一种可视化表现,这里的chart对象只是图表的展示对象去,其数据也相当于一个备份,改变chart属性时并没有实际改变源数据,所以只会在图形上一时的显示更改后的效果,不说这个过程有多复杂,最终也不能实际完成工作。
于是我在这里卡住了。

最终的解决

由于上述问题一时卡住了我,我就先去忙其他的活了,可这终究是一到坎,我想着越过它。于是顺着上面的思路,加上实际的操作,每次人工操作都是点击编辑数据之后,复制粘贴来完成。这样一想,python读了了ppt文件,将其转换为一个对象,表格,可以看得见的图标,都可以在这个对象中找到,那编辑数据处的原数据应该也在原始的shape对象中。我得找到它。
我查看了chart_part属性,这让我眼前一亮[‘before_marshal’, ‘blob’, ‘chart’, ‘chart_workbook’, ‘content_type’, ‘drop_rel’, ‘load’, ‘load_rel’, ‘new’, ‘package’, ‘part’, ‘part_related_by’, ‘partname’, ‘partname_template’, ‘relate_to’, ‘related_parts’, ‘rels’, ‘target_ref’]
这其中就有chart,我欣喜的是发现了chart_workbook,这让我想起了Excel,python中操作Excel的openpyxl中就有workbook。于是我得好好的找找如何修改这个Excel文件的方法。
初步我发现了chart_workbook有如下属性和方法

class ChartWorkbook(builtins.object)
 |  ChartWorkbook(chartSpace, chart_part)
 |  
 |  Provides access to the external chart data in a linked or embedded Excel
 |  workbook.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, chartSpace, chart_part)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  # 使用xlsx文件的blob格式数据更新这个workbook
 |  update_from_xlsx_blob(self, xlsx_blob)
 |      Replace the Excel spreadsheet in the related |EmbeddedXlsxPart| with
 |      the Excel binary in *xlsx_blob*, adding a new |EmbeddedXlsxPart| if
 |      there isn't one.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  xlsx_part
 |      Return the related |EmbeddedXlsxPart| object having its rId at
 |      `c:chartSpace/c:externalData/@rId` or |None| if there is no
 |      `<c:externalData>` element.

发现chart_workbook中有一个blob二进制文件,这里经过不断尝试确定excel的内容就是以二进制存在blob中,如此凭着试一试的心态,我以二进制形式读了一个xlsx文件,然后结合update_from_xlsx_blob方法,想着能不能更新数据。

wbda = open("1111.xlsx","rb").read()
# 遍历每一个幻灯片的形状
for shape in pager.shapes:
    # print(shape.has_chart)
    if shape.has_chart:
        # print(help(shape))
        chart_part = shape.chart_part
        chart_workbook = chart_part.chart_workbook
        chart_workbook.update_from_xlsx_blob(wbda)
        chart = shape.chart
        # 是否能直接更新表格不需要人工处理,实际并不能
        # chart.__init__(chartSpace=chart._chartSpace,chart_part=chart_part)

运行完成,打开新生产的文件,第一眼发现折线图并没有改变,觉得自己又失败了,可当我点击编辑数据后,折线图立刻发生了变化,播放PPT原来的折线图的动态过程还在。总算是大功告成了,唯一美中不足的就是最后还需要手动点一下编辑数据。当然这个可以交个Python去做,暂时想到的就是Python模拟人去点击,相关的知识下次再分享了。

小结

本次使用Python借助PPT模板自动生成同样样式的PPT。这其中走了很多弯路,让我学会了很多东西,强化了对一些知识的认识。
1:解决问题时要有不同的思路,目的是生成同样样式的PPT,那么可以尝试自己直接用Python生成,也可以直接对已经完成的模板进行修改。亦或者是不直接操作使用python-pptx库,使用自动化控制软件去模拟人的操作行为,等等,只要最终问题得到了解决那就已经是一种进步
2:一切皆对象,对象就有属性和方法,通过阅读API文档并结合实际操作可以快速的了解一个库。而实际对象转换时,肉眼看的见的数据或者图形都有可能转换成python中的对象,那么实际ppt所包含的字体,颜色,变宽,大小,图形,表格等都会在python转换的对象中存在,结合实际问题,就可以合理运用属性和方法来快速掌握一个库的使用。完成任务。

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...