在上一章,我们已经拿到了40G的数据,如何分析这些文档,是最主要的目标。由于网站在ppt文档中暴力插入了广告链接,删掉这些与内容无关的信息就是文档解析的第一步。
本文主要是两部分信息:
- 文档脏数据的定位与删除;
- MSOffice接口简单介绍;
1、“脏数据”都有什么?
就目标站点来说,往文档中插的信息无外乎这三类:
- 文档描述信息;
- 文本框连接;
- 广告页;
其中,文档描述信息不影响此次内容分析,暂不处理。剩余两类数据的清洗逻辑很简单,将在下一章介绍。
2、数据清洗的准备工作
就PowerPoint来说,已经提供了删除页面内文本框和删除Slide的接口。熟悉VBA的朋友(容易 暴露年龄)很容易在宏编辑中组织代码,进而在C++中实现完整逻辑。这次,我选择用python 实现,所有源码详见Git。
同时,我把VB代码在Git中也保留了一份,很方便您在宏编辑器中调试。
2.1、关于MSOffice Interface Reference
这部分的知识比较老,现在用的也不多。如果您刚接触,可以看我早先前在CSDN上的博 客——《北塔教你做插件》。如果只是查询接口,可以通过一下两个办法:
方法1、MSOffice2010 帮助文件
为什么不是其他版本?因为只有这个版本的帮助文件中提供了“开发人员参考”,点击“搜索”中的类型就可以找到。
帮助文档提供的信息还是比较丰富的,除了接口说明还有很多例子,方便理解各个对象间的关系,是帮助理解、提高效率的利器。
方法2、MSO 接口声明
我在Git中保存了MSO和MSPPT的接口声明,详见MSPPT_QuickReference目录。
如果您对VBA已经有些了解,且所使用的语言不是 C++的前提下,通过这两个文档查询接口与定义类型还是很方便的。比如,在Python中通过win32Com调用,所需的查询工作就可以在这两个文档中解决。
准备工作就写到这,如果您还不太懂也没关系,进入下一章,我带您用python调用MSO接口,完成这40G文档的数据清洗。
3、PPT文件的数据清洗
创建COfficeAdapter的目的,就是为了封装MSO接口,以实现文件打开、操作和关闭等行为。为数据清洗和文档快照提供支持。
源码如下:
#!/usr/bin/python
# -*- coding: gbk -*-
'''''
对MSOffice COM接口的封装,简单实现文件打开、操作和关闭等行为。
为数据清洗和快照提供支持。
'''
import win32com
from win32com.client import Dispatch, constants
import os
msoTrue = -1
class COfficeAdapter():
def __init__(self ):
print "init"
try:
self.m_App = win32com.client.Dispatch('PowerPoint.Application')
self.m_App.Visible = 1
except BaseException:
print "init Exception!!"
#隐式加载为0 ,显式加载为1
def __del__(self):
print "__del__"
try:
self.m_App.Quit()
except BaseException:
print "Quit Exception!!"
def OpenDoc(self, oPath):
oRet = False
if os.path.exists(oPath):
oPath = oPath.lower()
oNameSplit = os.path.splitext(oPath)
if (oNameSplit[1] == ".ppt" or oNameSplit[1] == ".pptx"):
try:
self.m_Doc = self.m_App.Presentations.Open(oPath)
oRet = True
except BaseException:
print "OpenDoc Exception!!"
return oRet
def SaveDoc(self, newfilename=None):
if newfilename:
self.m_Doc.SaveAs(newfilename)
else:
oRet = self.m_Doc.Save()
print "save:" ,oRet
def CloseDoc(self):
#print self.m_Doc.Saved
try:
self.m_Doc.Close()
except BaseException:
print "Close Exception!!"
# 删除文档中恶意广告信息,只要包含制定字符,即删除整个textRange。
# 此方法,主要针对"www.1ppt.com"。
# 有删除操作返回true
def RemoveTextRange(self, oKeyStr):
oRet = False
oSlideCounts = self.m_Doc.Slides.Count
#遍历每一页,方便对每页数据进行操作
for i in range(1 , oSlideCounts + 1):
#print i ,"页"
oSlide = self.m_Doc.Slides.Item(i)
oShapeCounts = oSlide.Shapes.Count
#print oShapeCounts
#遍历单页中所有shape
for j in range(1,oShapeCounts + 1):
oShape = oSlide.Shapes.Item(j)
#判断类型,找到文字
if oShape.TextFrame.HasText == msoTrue:
oTR = oShape.TextFrame.TextRange
try:
#oTextLen = oTR.Length
sText = oTR.Text
#转换为小写
sText = sText.lower()
#print sText
# 查找关键字,确认删除制定信息。
# 该方法并不用于最后一页,因为最后一页为整页的广告信息
oPos = sText.find(oKeyStr)
if oPos > 0 and i != oSlideCounts :
print "remove textRange" , i ,"+" ,oSlideCounts
oRet = True
oShape.Delete()
break
if oPos > 0 and i == oSlideCounts :
print "delete Slide"
oRet = True
oSlide.Delete()
break
except BaseException:
print "BaseException"
return oRet
def testDoc():
oFilePath = r"D:\pptSpider\PPTFile1\A版小学五年级下册语文PPT课件\2015语文A版语文五下《一双新鞋》ppt课件.pptx"
oKeyWork = "www.1ppt.com"
print oFilePath
oDoc = COfficeAdapter()
if oDoc.OpenDoc(oFilePath):
oRem = oDoc.RemoveTextRange(oKeyWork)
#只有当删除页面元素时,才进行文档保存操作
if oRem:
oDoc.SaveDoc()
oDoc.CloseDoc()
#testDoc()
接口分类比较清楚,具体内容就不讲了,需要注意两点:
- 异常处理。解析上万文档,什么类型都会遇到,就拿OpenDoc来说,含密码、需修复和 打开失败等,异常情况很多,如果要想让代码不崩不断,tryCatch是免不了的。
- MSO接口中,对象的删除是需要重建索引才能起作用的。在一套循环检索中, obj.delete()之后对象删了,但索引item还有。这个特点需要大家逐一。
关于多作业并行处理的优化方案
目前的实现逻辑是单作业串行操作,虽然文档处理速度是很快的,但由于文档量级较高,352个板块,3万份文档,一台PC机一天也没跑完。不过索性没断,就继续跑吧,等到数据分析的时候再做优化。
备注:
文档有了,脏数据也清干净了,下一步就是依据这些文档进行分析。抓的点我列了一半,周末再琢磨琢磨,下周就能开始跑了。
我争取尽快输出文档,和大家分享。同时,也欢迎各位提出自己的想法和意见。