2017-04-13 139 views
0

我在同一水平上有两种节点(这里foobar)的XML文件的工作,像这样:高效的方法来搜索包含节点的xml节点?

<foo> 
    <id>123</id> 
    <name>The first foo</name> 
</foo> 
<foo> 
    <id>456</id> 
    <name>The second foo</name> 
</foo> 
<bar> 
    <name>The first bar</name> 
    <foo>123</foo> 
</bar> 
<bar> 
    <name>The second bar</name> 
    <foo>123</foo> 
</bar> 

需要注意的是:

  • 一个foo对于每个bar;
  • 一个或几个bar关联到每个foo

我想获得,每个bar,对应foo节点,所以我写了这个代码:

import xml.etree.ElementTree as ET 
root = ET.fromstring(data) 
for bar in root.findall('bar'): 
    for foo in root.findall('foo'): 
     if foo.find('id').text == bar.find('foo').text: 
      foo_of_bar = foo 
      pass 
    print bar.find('name').text + ': ' + foo_of_bar.find('name').text 

结果:

The first bar: The first foo 
The second bar: The first foo 

但我问自己,如果有是一种更好的方式,使用XPath语法或更多pythonic代码。

+0

什么是ET?你能否显示进口? –

+0

ET for'xml.etree.ElementTree' – roipoussiere

+0

我们在谈论多少个节点?几百或几十万? – Tomalak

回答

1

所以,如果我理解正确的话,每个ID都会有至少一个foo,以及一个或多个bar秒。

因此,要组织这样的一个有效的方法是一本字典,如:

{ 
    id: (foo, (bar, bar, ...)), 
    id: (foo, (bar, bar, ...)), 
    ... 
} 

(或者你可以有富的关键)。

显然你必须寻找所有的foos来获得第一个元素。从那里,您可以为bar[foo='{id}']的XPath表达式执行findall搜索bar元素,foo子元素具有引号之间的完整内容。所以:

root = ET.fromstring(data) 
foo_bars = {} 
for foo in root.findall('foo'): 
    foo_id = foo.find('id').text 
    bars = tuple(root.findall("bar[foo='{}']".format(foo_id))) 
    foo_bars[foo_id] = (foo, bars) 
    # Do something with foo (one element), 
    # bars (a tuple of elements) and 
    # foo_id (A str of the id) 

for foo_id, f_bs in foo_bars.items(): 
    foo, bars = f_bs 
    # Also do something 

这避免了迭代,每栏元素的每个元素富,为富每一个元素,你只在必要的酒吧元素进行迭代。


或者,您可以迭代一次,并在找到元素时构建字典。对于较小的xml文件,这将会相当慢,但是如果你有更大的文件,速度可能会更快。

import xml.etree.ElementTree as ET 
import collections 

root = ET.fromstring(data) 
foo_bars = collections.defaultdict(lambda: [None, []]) 
for child in root: 
    if child.tag == 'foo': 
     # Found a new id 
     foo_bars[child.find('id').text][0] = child 
    elif child.tag == 'bar': 
     foo_bars[child.find('foo').text][1].append(child) 
    else: 
     # Possibly raise a ValueError? 
     pass 
for foo_id, f_bs in foo_bars.items(): 
    foo, bars = f_bs 
    # Do something