【问题标题】:Python: Parsing XML autoadd all key/value pairsPython:解析 XML 自动添加所有键/值对
【发布时间】:2015-02-13 16:03:00
【问题描述】:

我搜索了很久,尝试了很多!但我无法为这个完全简单的场景敞开心扉。我需要说我是一个 python 新手,但是一个非常好的 bash 编码器;o) 我已经用 python 编写了一些代码,但也许还有很多我需要学习的东西,所以不要对我太苛刻;o) 我'愿意学习,我阅读了 python 文档和许多示例,并自己尝试了很多,但现在我正处于我在黑暗中挑选的地步..

我解析作为 XML 提供的内容。它大约 20-50 MB 大。 我的 XML 示例:

<MAIN>
  <NOSUBEL>abcd</NOSUBEL>
  <NOSUBEL2>adasdasa</NOSUBEL2>
  <MULTISUB>
    <WHATEVER>
      <ANOTHERSUBEL>
        <ANOTHERONE>
          (how many levels can not be said / can change)
        </ANOTHERONE>
      </ANOTHERSUBEL>
     </WHATEVER>
   </MULTISUB>..
   <SUBEL2>
     <FOO>abcdefg</FOO>
   </SUBEL2>
   <NOSUBEL3>abc</NOSUBEL3>
   ... 
   and so on 
</MAIN>

这是解析它的主要部分(如果您需要更多详细信息,请询问):

from lxml import etree
resp = my.request(some call args)
xml = etree.XML(resp)

for element in xml.findall(".//MAIN"):

   # this works fine but is not generic enough:   
   my_dict = OrderedDict()
   for only1sub in element.iter(tag="SUBEL2"):
        for i in only1sub:
            my_dict[i.tag] = i.text

这与 1 个子元素一起工作得很好,但这意味着我需要知道树中哪个有子元素,哪个没有。这可能会在未来发生变化或被添加。 另一个问题是 MULTISUB。使用上面的代码,我只能解析到第一个标签。

目标

想要实现的目标是 - 充其量:

A) 有一个函数/代码 sn-p 能够解析整个 XML 内容,如果有一个子元素(例如,使用“if len(x)”或其他)然后解析到下一个级别,直到你到达没有子元素/树的级别。然后继续B)

B) 对于每个找到的没有子元素的 XML 标记,我想用标记名称和标记文本更新字典。

C) 我想对所有可用元素执行此操作 - 标签和直接子标签名称(例如“NOSUBEL2”或“MULTISUB”)将不会(经常)更改,因此它将是可以将它们用作解析的起点。

到目前为止,我尝试的是链接几个循环,例如 for 和 while 以及 for again 等等,但没有一个是完全成功的。我还潜入了 python 生成器,因为我认为我可以用 next() 函数做点什么,但什么也做不了。但同样,我可能不知道正确使用它们,所以我很高兴每个答案..

最后,我相信我需要的东西是如此简单。我只想从标签名称和标签内容中获得键值对,这不是那么难吗?任何帮助都非常感谢..

你能帮我实现目标吗?

(感谢您阅读到这里!)

【问题讨论】:

    标签: python xml parsing


    【解决方案1】:

    您正在寻找的是recursion - 一种在该过程内部运行某些过程的技术,但用于原始问题的子问题。在这种情况下:或者,对于某个元素的每个子元素运行此过程(如果有子元素)或使用元素的标签名称和文本更新您的字典。

    我假设最后你有兴趣让字典 (OrderedDict) 包含整个元素树的叶子的“平面表示”(没有子元素的节点)标签名称/文本值,在你的情况下,打印出来,看起来像这样:

    OrderedDict([('NOSUBEL', 'abcd'), ('NOSUBEL2', 'adasdasa'), ('ANOTHERONE', '(how many levels can not be said / can change)'), ('FOO', 'abcdefg'), ('NOSUBEL3', 'abc')])
    

    通常,您会定义一个函数,该函数将使用您的部分数据(在本例中:子元素,如果有的话)调用自身或执行某些操作(在本例中:更新某些字典实例)。

    由于我不知道my.request 调用背后的详细信息,我已根据您提供的内容,通过从包含有效 XML 的字符串中解析来替换它。只需替换构造tree 对象即可。

    resp = """<MAIN>
        <NOSUBEL>abcd</NOSUBEL>
        <NOSUBEL2>adasdasa</NOSUBEL2>
        <MULTISUB>
            <WHATEVER>
                <ANOTHERSUBEL>
                    <ANOTHERONE>(how many levels can not be said / can change)</ANOTHERONE>
                </ANOTHERSUBEL>
            </WHATEVER>
        </MULTISUB>
        <SUBEL2>
            <FOO>abcdefg</FOO>
        </SUBEL2>
        <NOSUBEL3>abc</NOSUBEL3>
    </MAIN>"""
    
    
    from collections import OrderedDict
    from lxml import etree
    
    
    def update_dict(element, my_dict):
        # lxml defines "length" of the element as number of its children.
        if len(element):  # If "length" is other than 0.
            for subelement in element:
                # That's where the recursion happens. We're calling the same
                # function for a subelement of the element.
                update_dict(subelement, my_dict)
    
        else:  # Otherwise, subtree is a leaf.
            my_dict[element.tag] = element.text
    
    
    if __name__ == "__main__":
        # Change/amend it with your my.request call.
        tree = etree.XML(resp)  # That's a <MAIN> element, too.
    
        my_dict = OrderedDict()
        # That's the first invocation of the procedure. We're passing entire
        # tree and instance of dictionary.
        update_dict(tree, my_dict)
    
        print(my_dict)  # Just to see that dictionarty was filled with values.
    

    如您所见,我没有在代码中使用任何标记名称(当然,XML 源除外)。

    我还添加了来自 collections 的缺失导入。

    【讨论】:

    • 哦...这看起来非常棒,而且您的解释非常好!真的非常感谢!我迫不及待地在我的代码中测试它,但我需要等到我的假期结束 :) 不过我会将它标记为已回答,因为它看起来完全符合我的要求。再次感谢您!
    猜你喜欢
    • 2014-03-29
    • 2014-06-18
    • 2017-08-24
    • 2021-12-10
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 2019-04-02
    • 1970-01-01
    相关资源
    最近更新 更多