【问题标题】:xml.etree.ElementTree get node depthxml.etree.ElementTree 获取节点深度
【发布时间】:2013-06-20 23:06:20
【问题描述】:

XML:

<?xml version="1.0"?>
<pages>
    <page>
        <url>http://example.com/Labs</url>
        <title>Labs</title>
        <subpages>
            <page>
                <url>http://example.com/Labs/Email</url>
                <title>Email</title>
                <subpages>
                    <page/>
                    <url>http://example.com/Labs/Email/How_to</url>
                    <title>How-To</title>
                </subpages>
            </page>
            <page>
                <url>http://example.com/Labs/Social</url>
                <title>Social</title>
            </page>
        </subpages>
    </page>
    <page>
        <url>http://example.com/Tests</url>
        <title>Tests</title>
        <subpages>
            <page>
                <url>http://example.com/Tests/Email</url>
                <title>Email</title>
                <subpages>
                    <page/>
                    <url>http://example.com/Tests/Email/How_to</url>
                    <title>How-To</title>
                </subpages>
            </page>
            <page>
                <url>http://example.com/Tests/Social</url>
                <title>Social</title>
            </page>
        </subpages>
    </page>
</pages>

代码:

// rexml is the XML string read from a URL
from xml.etree import ElementTree as ET
tree = ET.fromstring(rexml)
for node in tree.iter('page'):
    for url in node.iterfind('url'):
        print url.text
    for title in node.iterfind('title'):
        print title.text.encode("utf-8")
    print '-' * 30

输出:

http://example.com/article1
Article1
------------------------------
http://example.com/article1/subarticle1
SubArticle1
------------------------------
http://example.com/article2
Article2
------------------------------
http://example.com/article3
Article3
------------------------------

Xml 表示站点地图的树状结构。

我整天在 docs 和 Google 上翻来覆去,无法弄清楚如何获取条目的节点深度。

我使用了子容器的计数,但这仅适用于第一个父容器,然后它中断,因为我无法弄清楚如何重置。但这可能只是一个骇人听闻的想法。

想要的输出:

0
http://example.com/article1
Article1
------------------------------
1
http://example.com/article1/subarticle1
SubArticle1
------------------------------
0
http://example.com/article2
Article2
------------------------------
0
http://example.com/article3
Article3
------------------------------

【问题讨论】:

  • 您能提供一个示例 xml 吗?另外,您是否需要计算节点深度 - 我的意思是,是否可以解析 url 本身并计算域名后有多少项目?
  • 添加了 XML 示例。我使用的深度最大为 7,并且总页数不超过 500。
  • @tntu,示例似乎坏了。
  • ElementTree 元素在父节点上没有句柄,因此除非您创建某种节点映射,否则不可能返回树来计算特定节点的深度。不过你可以construct that mapping yourself

标签: python xml xml-parsing


【解决方案1】:

Python ElementTree API 为 XML 树的深度优先遍历提供迭代器 - 不幸的是,这些迭代器不向调用者提供任何深度信息。

但是你可以编写一个深度优先的迭代器,它也返回每个元素的深度信息:

import xml.etree.ElementTree as ET

def depth_iter(element, tag=None):
    stack = []
    stack.append(iter([element]))
    while stack:
        e = next(stack[-1], None)
        if e == None:
            stack.pop()
        else:
            stack.append(iter(e))
            if tag == None or e.tag == tag:
                yield (e, len(stack) - 1)

请注意,这比通过跟踪父链接(使用 lxml 时)确定深度更有效 - 即它是 O(n)O(n log n)

【讨论】:

  • 该代码不适用于 python2。 iter(e)´´ is deprecated and should be replaced by e.iter()´´ 但它仍然没有按预期工作。你能再检查一下吗?
  • @ChristianK。您在哪里读到 iter() 已弃用?我最初为 Python 3 编写了代码。但我只是在 Python 2.7.15 下对其进行了测试,它无需任何修改即可为我工作:root = ET.fromstring('&lt;root&gt;&lt;foo&gt;&lt;bar&gt;1&lt;/bar&gt;&lt;/foo&gt;&lt;/root&gt;') 这样list(depth_iter(root)) 如预期的那样返回一个包含所有元素和适当深度信息的列表。
  • 对不起,完全是我的错。代码按预期工作。但是上面的xml格式不正确。
  • @ChristianK。畸形是什么意思?例如,xmllint 愉快地处理它。
  • 第10行的&lt;page&gt;标签没有结束标签,我认为&lt;subpages/&gt;不是一个正确的结束标签。
【解决方案2】:
import xml.etree.ElementTree as etree
tree = etree.ElementTree(etree.fromstring(rexml)) 
maxdepth = 0
def depth(elem, level): 
   """function to get the maxdepth"""
    global maxdepth
    if (level == maxdepth):
        maxdepth += 1
   # recursive call to function to get the depth
    for child in elem:
        depth(child, level + 1) 


depth(tree.getroot(), -1)
print(maxdepth)

【讨论】:

  • 始终建议至少添加对所提供代码的最小功能描述,解释它如何回答问题。
【解决方案3】:

使用lxml.html

import lxml.html

rexml = ...

def depth(node):
    d = 0
    while node is not None:
        d += 1
        node = node.getparent()
    return d

tree = lxml.html.fromstring(rexml)
for node in tree.iter('page'):
    print depth(node)
    for url in node.iterfind('url'):
        print url.text
    for title in node.iterfind('title'):
        print title.text.encode("utf-8")
    print '-' * 30

【讨论】:

  • 比 'node.xpath("count(ancestor::*)")' 或 'len(tree.getelementpath(node).split("/"))' 快得多。谢谢:)。
  • 这并没有真正回答问题,因为 OP 询问如何使用 from xml.etree import ElementTree as ET 实现它,而不是 lxml.html
【解决方案4】:

我的方法,递归函数以级别列出。您必须首先设置您要传递的节点的初始部门:

# Definition of recursive function
def listchildrens(node,depth):
    # Print node, indent with depth 
    print(" " * depth,"Type",node.tag,"Attributes",node.attrib,"Depth":depth}
    # If node has childs, recall function for the node with existing depth
    if len(node) > 0:
        # Increase depth and recall function
        depth+= 1
        for child in node:
            listchildrens(node,depth)
# Define starting depth
startdepth = 1
# Call the function with the XML body and starting depth
listchildrens(xmlBody,startdepth)

【讨论】:

    【解决方案5】:

    lxml 最适合这个,但是如果你必须使用标准库,就不要使用 iter 并去走树,这样你就可以知道你在哪里。

    from xml.etree import ElementTree as ET
    tree = ET.fromstring(rexml)
    
    def sub(node, tag):
        return node.findall(tag) or []
    
    def print_page(node, depth):
        print "%s" % depth
        url = node.find("url")
        if url is not None:
            print url.text
        title = node.find("title")
        if title is not None:
            print title.text
        print '-' * 30
    
    def find_pages(node, depth=0):
        for page in sub(node, "page"):
            print_page(page, depth)
            subpage = page.find("subpages")
            if subpage is not None:
                find_pages(subpage, depth+1)
    
    find_pages(tree)
    

    【讨论】:

      【解决方案6】:

      这是不使用 XML 库的另一种简单方法:

      depth = 0
      for i in range(int(input())):
          tab = input().count('    ')
          if tab > depth:
              depth = tab
      print(depth)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-14
        • 2013-06-13
        • 2015-05-29
        • 2022-01-13
        • 1970-01-01
        • 1970-01-01
        • 2012-04-22
        • 1970-01-01
        相关资源
        最近更新 更多