用Python的minidom解析XML,以Wikipedia Miner为例

最近需要用Python解析XML,用minidom进行解析。

需要解析的是Wikipedia Miner这个服务返回的XML文件,有几种不同结构的XML文件。这次是用的是xml.dom.mimidom这个模块,存在于Python标准库当中,所以调用非常方便。

首先import这个库。import xml.dom.minidom

需要解析的文件应当以DOM对象形式存在,如此导入: xmlfile = xml.dom.minidom.parse(sourcefile) rootnodes = xmlfile.documentElement 其中sourcefile是XML文件,可以下载于Internet,也可以是本地文件。我需要直接操作来源于Wikipedia Miner的Web服务的XML文件,所以之前先用下面代码获取文件: from urllib2 import Request, urlopen req = Request('http://wikipedia-miner.cms.waikato.ac.nz/services/search?query=hiking%20new%20zealand&complex=true') try: sourcefile = urlopen(req) except IOError, e: if hasattr(e, 'reason'): print 'We failed to reach a server.' print 'Reason: ', e.reason elif hasattr(e, 'code'): print 'The server couldn't fulfill the request.' print 'Error code: ', e.code sourcefile = urlopen(req) 这段就是返回http://wikipedia-miner.cms.waikato.ac.nz/services/search?query=hiking%20new%20zealand&complex=true处(这是一个例子)的XML文件,并存于变量sourcefile中。

首先解析底层对象 rootnodes = xmlfile.documentElement#The root layer labelnodes = rootnodes.getElementsByTagName('label')#Generate a list and store all 'label' nodes 上面代码首先读取文档元素,然后以此为基础,找出下面一层中标签名为“label”的元素,并且储存于一个列表中。之后我们如果需要遍历所有标签名为“label”的元素,那只需遍历这个列表就可以了。

Wikipedia Miner接收到一个搜索请求(本例中是搜索“hiking new zealand”),然后对输入的字符串进行分词,分词后的每一个最小单位就储存于一个label元素中。也就是我们刚刚解析并存在一个列表里面的东西。下面遍历这个列表,提取出每个label的搜索结果。

label下是senses元素,senses元素下是sense元素。每个sense元素分别代表一个可能的相关Wikipedia条目。我们需要对每个label获得它的每个sense,再有组织地存储,这样就达到了调用Wikipedia Miner服务的目的。

思路就是遍历每个label,对每个label元素,读取并存储senses元素。对senses元素,读取并存储每个sense元素。之后遍历sense元素,获得我们需要的属性(id、title等等)并存储。 result = [] for labels in labelnodes:#Go over all labels labelinfo = {}#Init a dict labelinfo['labelName'] = labels.getAttribute('text') labelinfo['linkProbability'] = float(labels.getAttribute('linkProbability'))#Store basic info in the dict labelinfo['labelSenses'] = [] labelSensesTag = labels.getElementsByTagName('senses')#Peel the first layer labelSenseList = labelSensesTag[0].getElementsByTagName('sense')#Finally get the sense list of a label 上面的代码中,先创建一个列表用于存储最终的结果。然后开始对label遍历。对其中每个label,创建一个叫labelinfo的字典用于分门别类存储信息。用.getAttribute()来获得label元素的各个属性,并存入labelinfo字典。而对于labelSenses的值,则又需要提取label下的senses元素,再提取senses元素下的sense元素,对sense元素进行遍历才可得到。所以,上面代码中,labelSenseList最终是一个存储了各个sense元素的列表。

接下来开始遍历sense元素。 for senses in labelSenseList:#Go over all possible senses of a label senseinfo = {}#Create a dict to store info of a sense senseinfo['artID'] = int(senses.getAttribute('id'))#Get every piece of info and add to the dict senseinfo['artTitle'] = senses.getAttribute('title') senseinfo['probability'] = float(senses.getAttribute('priorProbability')) senseinfo['weight'] = float(senses.getAttribute('weight')) labelinfo['labelSenses'].append(senseinfo) 对每个sense元素,先建立senseinfo字典准备存储,在存入所有信息之后,再通过.append()将senseinfo字典作为上面labelinfo['labelSenses']列表的一个值存入其中。

到这里为止,我们已经提取了所有有需要的信息,并以一个合理的,方便调用的结构存在内存中。

观察整个处理过程,除了最初的导入模块和需要处理的文档之外,其他操作需要按照不同的XML文档的内容来改变。基本上,同类的元素用列表来存储,一个元素的不同属性(或值,本例中没有涉及到值的读取)用字典来存储。同时,使用.getElementsByTagName()来获得下层的元素,也可以通过.childNodes(本例中未涉及到)来直接获得子元素的列表。 在这里有一个易错点。无论查找到符合条件的子元素有多少个,.getElementsByTagName()返回的都是一个列表,所以不要忘记用list[0]来读取真正的XML元素。当列表中只有一项时,这个操作还是容易忘记的

完整例子:https://github.com/yangl1996/WikiHack/blob/master/xmlextract.py