我需要定期从管理软件中导出XML文件。
这是我第一次在Python中使用XML Parsing。带有xml.sax
的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经验,我想知道这是“普遍接受”还是“最好”的方式?
谢谢:)
答案 0 :(得分:4)
我也使用了sax,但后来我找到了一个更好的工具:iterparse from ElementTree。
它类似于sax,但是您可以检索包含内容的元素,以释放您只需要在检索后清除元素的内存。
答案 1 :(得分:2)
您是否需要为此使用SAX?
因为,如果将整个XML文件加载到内存中对象模型中是可以接受的,那么您可能会发现使用ElementTree DOM API要容易得多。
(如果您在给定子节点时不需要检索父节点的能力,Python标准库中的cElementTree应该很好地完成这个操作。如果这样做,LXML库提供了一个ElementTree实现,它将为您提供父引用。两者都使用编译的C模块来提高速度。)
答案 2 :(得分: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'))
......等等......
答案 3 :(得分:1)
我认为最简单的解决方案正是您在示例中所做的 - 维护一堆节点。