使用Python xml.sax解析XML:如何“跟踪”您在树中的位置?
我需要定期从我们的管理软件中导出XML文件。
这是我第一次在Python中使用XML解析。用xml.sax
处理XML并不是特别难,但我想知道,怎么才能“跟踪”我在XML树中的位置呢?
举个例子,我有一个客户列表。我想提取电话号码,但在XML中有多个地方出现了电话号码:
eExact -> Accounts -> Account -> Contacts -> Contact -> Addresses -> Address -> Phone
eExact -> Accounts -> Account -> Contacts -> Contact -> Phone
eExact -> Accounts -> Account -> Phone
所以我需要知道自己在XML树的哪个位置,才能获取到正确的电话号码。
根据我从Python网站上查到的xml.sax文档,似乎没有什么“简单”的方法或变量可以用来记录这个位置。
所以,我做了以下的事情:
import xml.sax
class Exact(xml.sax.handler.ContentHandler):
def __init__(self):
self.curpath = []
def startElement(self, name, attrs):
self.curpath.append(name)
if name == 'Phone':
print self.curpath, name
def endElement(self, name):
self.curpath.pop()
if __name__ == '__main__':
parser = xml.sax.make_parser()
handler = Exact()
parser.setContentHandler(handler)
parser.parse(open('/home/cronuser/xml/mount/daily/debtors.xml'))
这并不算太难,但因为我对XML的经验不多,我在想这样做是否是“普遍认可”或“最佳的”方式呢?
谢谢 :)
4 个回答
2
你为什么一定要用SAX呢?
因为如果把整个XML文件加载到内存中的对象模型里是可以接受的,那你可能会发现使用ElementTree DOM API要简单得多。
(如果你不需要通过子节点来获取父节点,Python标准库里的cElementTree就能很好地满足你的需求。如果你需要这个功能,LXML库提供的ElementTree实现可以让你获取父节点的引用。这两个都使用了编译的C模块来提高速度。)
4
我之前也用过sax,但后来发现了一个更好的工具:ElementTree里的iterparse。
这个工具和sax有点像,但你可以直接获取带内容的元素。为了释放内存,你只需要在获取元素后清除它就可以了。
2
感谢大家的评论。
我看了看ElementTree的iterparse,但那时候我已经在用xml.sax写了不少代码。因为iterparse带来的直接好处不大,我决定继续使用xml.sax。这已经比我之前的解决方案好很多了。
好了,最后我做了这些。
class Exact(xml.sax.handler.ContentHandler):
def __init__(self, stdpath):
self.stdpath = stdpath
self.thisrow = {}
self.curpath = []
self.getvalue = None
self.conn = MySQLConnect()
self.table = None
self.numrows = 0
def __del__(self):
self.conn.close()
print '%s rows affected' % self.numrows
def startElement(self, name, att):
self.curpath.append(name)
def characters(self, data):
if self.getValue is not None:
self.thisrow[self.getValue.strip()] = data.strip()
self.getValue = None
def endElement(self, name):
self.curpath.pop()
if name == self.stdpath[len(self.stdpath) - 1]:
self.EndRow()
self.thisrow = { }
def EndRow(self):
self.numrows += MySQLInsert(self.conn, self.thisrow, True, self.table)
#for k, v in self.thisrow.iteritems():
# print '%s: %s,' % (k, v),
#print ''
def curPath(self, full=False):
if full:
return ' > '.join(self.curpath)
else:
return ' > '.join(self.curpath).replace(' > '.join(self.stdpath) + ' > ', '')
然后我为不同的XML文件多次对这个进行子类化:
class Debtors(sqlimport.Exact):
def startDocument(self):
self.table = 'debiteuren'
self.address = None
def startElement(self, name, att):
sqlimport.Exact.startElement(self, name, att)
if self.curPath(True) == ' > '.join(self.stdpath):
self.thisrow = {}
self.thisrow['debiteur'] = att.get('code').strip()
elif self.curPath() == 'Name':
self.getValue = 'naam'
elif self.curPath() == 'Phone':
self.getValue = 'telefoon1'
elif self.curPath() == 'ExtPhone':
self.getValue = 'telefoon2'
elif self.curPath() == 'Contacts > Contact > Addresses > Address':
if att.get('type') == 'V':
self.address = 'Contacts > Contact > Addresses > Address '
elif self.address is not None:
if self.curPath() == self.address + '> AddressLine1':
self.getValue = 'adres1'
elif self.curPath() == self.address + '> AddressLine2':
self.getValue = 'adres2'
else:
self.getValue = None
if __name__ == '__main__':
handler = Debtors(['Debtors', 'Accounts', 'Account'])
parser = xml.sax.make_parser()
parser.setContentHandler(handler)
parser.parse(open('myfile.xml', 'rb'))
... 以此类推 ...