使用findall,Lxml迭代Xml

时间:2014-10-30 10:07:15

标签: python xml python-2.7 lxml findall

我有以下xml:

<head>
  <body>
    <para>
      <Run>
        <Runprop>
           <highlight val="red"/>
        <break/>
        <text>
         Hello there
        </text>
        </RunProp>
      </Run>
      <Run>
        <break/>
      </Run>
      <Run>
         <text>
          See you there
         </text>
      </Run>
    </para> ..
  </body>
</head>  

我想提取所有带有highlight“红色”值的文字。请注意,highlight标记的级别低于文本标记的级别。条件是:

  1. 对于每个段落,添加一个额外的空格。
  2. 如果在迭代highlight标记的父项时遇到break标记,请添加空格。
  3. 仅提取与highlight标记
  4. 对应的文字

    我所做的是:

    text=""                                #initialize an empty string
    for p in lxml_tree.findall('para'): #itertate over each paragraph (all paragarpahs have the same tag name para)
        for r in p.findall("Run"):     #iterate over each run
             for a in r.iter(tag="highlight"): #search for highlight tag
                for b in a.iterancestors(): #go back to the parents
                    if b.tag=="break":     #if break found
                       text+=" "           # add a space
                    elif b.tag=="text":    # if text found
                       text+=''.join(b.text) #add text 
    

    以上似乎不起作用,因为iterancestors一直到达根节点。我怎么可能迭代父母,Runpropbreaktext?我已经为所有文本实现了类似的内容,并且有效。

    编辑1
    上面只是一个有缺陷的逻辑,我宁愿迭代段落中的每个Run,首先搜索break,然后查看Runprop中是否有突出显示,然后在父亲的兄弟中提取文本

2 个答案:

答案 0 :(得分:2)

我已经设法解决了一些想法,并从anzel的答案中得到了一个想法。

text=""          
for p in lxml_tree.findall('para'):   #iterate over paragraphs
    text+= " "                        #add spaces
    for r in p.findall("Run"):        #iterate over each run in para
         for a in r.findall("break"):  #search for break tag in it and add space if found
            text+= " "
         for b in r.findall('.//highlight[@val="red"]/../..//text'): #search for red highlight in that run and return text
             text+=''.join(b.text) # append text to main string

答案 1 :(得分:1)

由于您的xml具有<highlight><break /><text>的位置模式,因此您实际上不需要返回父级。

我将使用itergetnext来实现您的需求:

from lxml import etree

html = '''
<head>
  <body>
    <para>
      <Run>
        <RunProp>
           <highlight val="red" />
        <break/>
        <text>
         Hello there
        </text>
        </RunProp>
      </Run>
      <Run>
        <break/>
      </Run>
      <Run>
         <text>
          See you there
         </text>
      </Run>
    </para> ..
  </body>
</head>'''

tree = etree.fromstring(html)

for node in tree.iter():
    if node.tag == 'para':
        node.text = '..your space here..' + node.text
        print node.text
    if node.tag == 'highlight':
        print node.values()
        if node.getnext().tag == 'break':
            print node.getnext().tag
            if node.getnext().getnext().tag == 'text':
                node.getnext().getnext().text = \
                    '..your space here..' + node.getnext().getnext().text
                print node.getnext().getnext().text
        elif node.getnext().tag == 'text':
            print node.getnext().text

..your space here....your space here..

['red']
break
..your space here....your space here..
         Hello there

将更改写入文件:

etree.ElementTree(tree).write('output.xml', pretty_print=True)

cat output.xml
<head>
  <body>
    <para>..your space here..
      <Run>
        <RunProp>
           <highlight val="red"/>
        <break/>
        <text>..your space here..
         Hello there
        </text>
        </RunProp>
      </Run>
      <Run>
        <break/>
      </Run>
      <Run>
         <text>
          See you there
         </text>
      </Run>
    </para> ..
  </body>
</head>