在Python 2.7中高效读取800GB XML文件

21 投票
3 回答
15182 浏览
提问于 2025-04-17 15:48

我正在用 Python 2.7 读取一个 800 GB 的 XML 文件,并使用 etree 的迭代解析器来解析它。

目前,我只是用 open('foo.txt') 来打开文件,没有使用任何缓冲参数。我有点困惑,不知道这样做是否合适,还是应该使用缓冲参数,或者用一些来自 io 的东西,比如 io.BufferedReader、io.open 或 io.TextIOBase。

如果能给我一些正确的方向建议,我会非常感激。

3 个回答

1

我没有尝试过处理那么大的xml文件,但上次我处理一些大(而且相对简单)的xml文件时,我用了一个叫做sax解析器的工具。

这个工具基本上会在每发生一个“事件”时给你一个回调,让你自己决定怎么保存需要的数据。你可以直接给它一个打开的文件,这样就不需要一次性把整个文件读进来。

1

你试过懒加载函数吗?可以看看这个链接:在Python中读取大文件的懒加载方法

这个方法似乎已经能解决你的问题了。不过,我建议你可以考虑用这个方法把数据写入数据库,mysql是免费的:http://dev.mysql.com/downloads/。另外,NoSQL也是免费的,可能更适合处理像800GB这样的大量数据:http://www.oracle.com/technetwork/database/nosqldb/downloads/default-495311.html

17

标准的 open() 函数 默认情况下会返回一个缓冲文件(如果你的平台支持的话)。对于文件对象来说,这通常是完全缓冲的。

这里的“通常”是指 Python 将这个决定留给 C 标准库的实现;它使用 fopen() 调用(在 Windows 上是 wfopen(),以支持 UTF-16 文件名),这意味着文件的默认缓冲区大小是由系统选择的;在 Linux 上,我认为这个大小是 8KB。对于像 XML 解析这样的纯读取操作,这种缓冲方式正是你想要的。

使用 iterparse 进行的 XML 解析会以 16384 字节(16KB)为单位读取文件。

如果你想控制缓冲区大小,可以使用 buffering 这个关键字参数:

open('foo.xml', buffering=(2<<16) + 8)  # buffer enough for 8 full parser reads

这将覆盖默认的缓冲区大小(我预计它会与文件块大小相匹配或是其倍数)。根据 这篇文章,增加读取缓冲区的大小应该会有所帮助,使用一个至少是预期读取块大小的 4 倍加 8 字节的大小会提高读取性能。在上面的例子中,我将其设置为 ElementTree 读取大小的 8 倍。

io.open() 函数 代表了新的 Python 3 输入输出结构,其中输入输出被分成了一个新的类层次结构,以提供更多的灵活性。代价是更多的间接调用,数据需要经过更多的层次,Python 的 C 代码需要自己做更多的工作,而不是将这些工作交给操作系统。

你可以尝试看看 io.open('foo.xml', 'rb', buffering=2<<16) 是否会表现得更好。以 rb 模式打开会给你一个 io.BufferedReader 实例

你不想使用 io.TextIOWrapper;底层的 expat 解析器需要原始数据,因为它会自己解码你的 XML 文件编码。使用它只会增加额外的开销;如果你以 r(文本模式)打开文件,就会得到这种类型。

使用 io.open() 可能会给你更多的灵活性和更丰富的 API,但底层的 C 文件对象是通过 open() 而不是 fopen() 打开的,所有的缓冲都由 Python 的 io.BufferedIOBase 实现来处理。

我认为你面临的问题是处理这个庞然大物,而不是文件读取。读取一个 800GB 的文件时,磁盘缓存几乎会被耗尽。

撰写回答