Python:解析XML自动添加所有键/值对

时间:2015-02-13 16:03:01

标签: python xml parsing

我搜索了很久,并尝试了很多!但我无法让这个完全容易的场景开放。我需要说我是一个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)有一个函数/代码片段能够解析整个XML内容,如果有子元素(例如“if len(x)”或其他),则解析到下一个级别,直到达到一个级别没有子元素/树。然后继续B)

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

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

到目前为止,我尝试过连接几个循环,例如for和while,以及再次等等,但没有一个完全成功。我也潜入python生成器,因为我认为我可以使用next()函数做一些事情,但也没有。但是我可能还没有正确使用它们的知识,所以我很高兴每个答案..

最后,我需要的东西是如此容易,我相信。我只想让标签名称和标签内容中的键值对不能那么难?任何帮助非常感谢..

你能帮我实现这个目标吗?

(已经感谢读到这里了!)

1 个答案:

答案 0 :(得分:1)

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

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

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的遗失导入。