用Python清理大型XML文件(流解析)

时间:2015-11-14 02:44:23

标签: python xml

我尝试使用Python来清理一些凌乱的XML文件,它做了三件事:

  1. 将40%-50%标签名称从大写转换为小写
  2. 删除标记之间的NULL
  3. 删除标记之间的空行
  4. 我在使用BeautifulSoup时这样做了,但是,由于我的一些XML文件超过1GB,我遇到了内存问题。相反,我研究了一些像xml.sax这样的流方法,但我没有完全接受这种方法。那么有人可以给我一些建议吗?

    xml_str = """
    <DATA>
    
        <ROW>
            <assmtid>1</assmtid>
            <Year>1988</Year>
        </ROW>
    
        <ROW>
            <assmtid>2</assmtid>
            <Year>NULL</Year>
        </ROW>
    
        <ROW>
            <assmtid>2</assmtid>
            <Year>1990</Year>
        </ROW>
    
    </DATA>
    """
    
    xml_str_update = re.sub(r">NULL", ">", xml_str)
    soup = BeautifulSoup(xml_str_update, "lxml")
    print soup.data.prettify().encode('utf-8').strip()
    

    更新

    经过一些测试并从Jarrod Roberson那里得到建议后,下面是一个可能的解决方案。

    import os
    import xml.etree.cElementTree as etree
    from cStringIO import StringIO
    
    def getelements(xml_str):
        context = iter(etree.iterparse(StringIO(xml_str), events=('start', 'end')))
        event, root = next(context)
    
        for event, elem in context:
            if event == 'end' and elem.tag == "ROW":
                elem.tag = elem.tag.lower()
                elem.text = "\n\t\t"
                elem.tail = "\n\t"
    
                for child in elem:
                    child.tag = child.tag.lower()
                    if child.text == "NULL":
                        # if do not like self-closing tag, 
                        # add &#x200B;, which is a zero width space
                        child.text = ""  
                    if child.text == None:
                        child.text = ""
                    # print event, elem.tag
                yield elem
                root.clear()
    
    with open(pth_to_output_xml, 'wb') as file:
        # start root
        file.write('<data>\n\t')
        for page in getelements(xml_str):
            file.write(etree.tostring(page, encoding='utf-8'))
        # close root
        file.write('</data>')
    

1 个答案:

答案 0 :(得分:1)

迭代解析

  

当不希望构建内存中的树时   实际上,使用不依赖的迭代解析技术   读取整个源文件。 lxml提供两种方法:供应   目标解析器类使用iterparse方法

import xml.etree.ElementTree as etree
for event, elem in etree.iterparse(xmL, events=('start', 'end', 'start-ns', 'end-ns')):
  print event, elem

以下是关于如何执行此操作的very complete tutorial

  

这将一次解析XML文件并将其提供给您   每一步都是如此。当标签第一时,start将触发   遇到。此时elem将为空,除了elem.attrib   包含标签的属性。结束时会触发   遇到了结束标记,并且已经读取了中间的所有内容。

然后在您的事件处理程序中,您只需在遇到转换后的信息时写出。