将大型xml文件分成n组

时间:2018-06-22 23:49:49

标签: python xml-parsing elementtree

我有一个较大的xml文件,其父标签具有97k个子标签。我想分成10个文件,每个文件有1万个标签,最后一个有剩余的文件。

我有这段代码,可以在每个文件中写入一个子标签,但无法进行分组。

因此,假设我的示例XML具有10个子标记,并且我想创建5个文件,每个文件具有2个子标记。

我的示例XML:

<root>
    <row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
<row>
        <NAME>A</NAME>
        <FIRSTNAME>A</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
    <row>
        <NAME>B</NAME>
        <FIRSTNAME>B</FIRSTNAME>
        <GENDER>M</GENDER>
    </row>
</root>

我的结果应该是5个文件,每个文件有2个条目,如下所示:

<root>
        <row>
            <NAME>A</NAME>
            <FIRSTNAME>A</FIRSTNAME>
            <GENDER>M</GENDER>
        </row>
        <row>
            <NAME>B</NAME>
            <FIRSTNAME>B</FIRSTNAME>
            <GENDER>M</GENDER>
        </row>
</root>

下面的代码在每个文件中放置每个子标记,但我想在此例如每个文件2个标记。

import xml.etree.ElementTree as ET
context = ET.iterparse('file.xml', events=('end', ))
index = 0
for event, elem in context:
    if elem.tag == 'row':
        index += 1
        filename = format(str(index) + ".xml")
        with open(filename, 'wb') as f:
            f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
            f.write(ET.tostring(elem))

谢谢!

编辑以添加配方:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

1 个答案:

答案 0 :(得分:1)

您有一个(事件,元素)对可迭代:

context = ET.iterparse('file.xml', events=('end', ))

现在,您希望将其过滤为row个元素:

rows = (elem for event, elem in context if elem.tag == 'row')

现在,您要将它们分组。使用the grouper recipe from the itertools docs

groups = grouper(rows, 2)

一旦工作正常并且想要真正运行,您显然可以将2更改为1000或其他任何内容。

现在,您可以仅迭代组。在此过程中,请使用enumerate,这样您就不需要手动的index += 1东西了。此外,让我们只使用format。而不是手动构建一个字符串然后无意义地调用an f-string

for index, group in enumerate(groups):
    # If you need to run on 3.5 or 2.7, use "{}.xml".format(index)
    filename = f"{index}.xml"
    with open(filename, 'wb') as f:
        f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")

…然后迭代组中的元素-但要小心;如果元素数量奇数,则grouper将使用None值填充不完整的最后一组。 1

        for elem in group:
            if elem:
                f.write(ET.tostring(elem))

1。并不是很难更改,但是我直接在文档之外使用配方,因此不必解释如何更改。