【问题标题】:Building large xml file with python 2使用 python 2 构建大型 xml 文件
【发布时间】:2018-03-13 11:51:59
【问题描述】:

我正在尝试获得在 Python 2/Django 中构建大型 XML 文件的最佳性能。

最终的 XML 文件约为 500mb。使用的第一种方法是使用 lxml,但它花费了 3.5 多个小时。我用 xml.sax (XMLGenerator) 进行了测试,花费了大约相同的时间,3.5 小时。

我正在尝试以最少的内存消耗找到最快的方法。我搜索了几天以找到最佳解决方案,但没有成功。

lxml 代码:

from lxml import etree

tree_var = etree.Element("tree_var", version='1.2')
DATE = etree.SubElement(DATETIME, "DATE")
DATE.text = datetime.date.today().strftime('%Y-%m-%d')
products = FromModel.objects.all().values_list('product_id')
for product in products:
if product.state == 'new':
    ARTICLE = etree.SubElement(tree_var, "ARTICLE", mode=product.state)

XMLGenerator 代码:

from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesNSImpl

with open("tmp/" + filename + ".xml", 'wb') as out:

    g = XMLGenerator(out, encoding='utf-8')
    g.startDocument()

    def start_tag(name, attr={}, body=None, namespace=None):
        attr_vals = {}
        attr_keys = {}
        for key, val in attr.iteritems():
            key_tuple = (namespace, key)
            attr_vals[key_tuple] = val
            attr_keys[key_tuple] = key

        attr2 = AttributesNSImpl(attr_vals, attr_keys)
        g.startElementNS((namespace, name), name, attr2)
        if body:
            g.characters(body)

    def end_tag(name, namespace=None):
        g.endElementNS((namespace, name), name)

    def tag(name, attr={}, body=None, namespace=None):
        start_tag(name, attr, body, namespace)
        end_tag(name, namespace)

g.endDocument()

我很确定 xml.sax 正在使用更少的内存,并且它会实时增加文件。另一方面,lxml 仅在循环结束时创建文件,使用巨大的缓冲区。

有什么建议吗?

谢谢!

【问题讨论】:

  • 看看这个docs.djangoproject.com/en/2.0/howto/outputting-csv/… 我知道 csv 文件,但你可以更改 xml 文件的内容
  • 看看XMLGenerator 类。您可以找到如何使用它的示例here
  • @Reidel:好的,我会检查那个。谢谢我用我的代码示例更新了我的线程。
  • 另一个资源可能有用d.cxcore.net/Python/Python_Cookbook_3rd_Edition.pdf 并查看CHAPTER 12 Concurrency 中的12.8. Performing Simple Parallel Programming 部分
  • 感谢@Reidel 的提示我创建了一个新代码,其中我从 3h30 改进到不到 1 小时(使用 8 核和 2 核测试)。他来了:import codecs from multiprocessing.dummy import Pool, cpu_count def do_work(products): <write to file> def parallel(result=None): pool = Pool(cpu_count()-1) # to prevent GIL with codecs.open(filename.xml", 'w+', "utf-8") as fp: <do stuff> pool.map(do_work, loop_object) pool.close() pool.join() parallel() 我认为它可以改善内存,但我很高兴我节省了创建 XML 的时间。

标签: python xml multiprocessing lxml concurrent.futures


【解决方案1】:

阅读时您可能仍在加载整个输入文件。您可以尝试以下几行,以获取增量读取文件的示例。 This stackoverflow 问题也提供了比下面的 sn-p 更多的上下文。您还可以在iterparse() 中指定一个目标元素,以便一次转储整个目标元素。

for event, elem in etree.iterparse(in_file, events=('start', 'end',)):
    if event == 'start':
        start_tag(elem.tag, {}, elem.text)
    elif event == 'end':
        end_tag(elem.tag)

    # It's safe to call clear() here because no descendants will be accessed
    elem.clear()

    # Also eliminate now-empty references from the root node to elem
    while elem.getprevious() is not None:
        del elem.getparent()[0]

【讨论】:

  • 谢谢,但这不是阅读的例子吗?我使用带有 iterparse 的事件方式(开始/结束)来增量读取。我认为这对写作不起作用......
  • 您将同时读取和写入,对start_tag()end_tag() 的调用将是写入。如果你提供一个 xml 示例,我可能会更具体一点。
  • 我明白了.. 需要尝试。感谢您的反馈。很快我会把最好的存档测试结果放在这里:)
  • 好的,我在这里挣扎。一个简单的 xml 结构可能是这样的:<?xml version='1.0' encoding='UTF-8'?> <FIRST version="1.2"> <SECOND> <THIRD/> </SECOND> <FOURTH mode="new"> <FIVE><![CDATA[_using_CDATA_example_]]></FIVE> </FOURTH> </FIRST> 我试图放 2 个示例进行比较:一个与来自 concurrent.futures 的 ProcessPoolExecutor (似乎这是一个 Python3 库)和你给我的示例,因为开始/结束是解析有用,所以可能是写(用 clear() )
  • 有没有重复的结构?我的意思是这种情况发生了多次。
猜你喜欢
  • 2013-05-19
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多