在Python 2.7中高效读取800GB XML文件
我正在用 Python 2.7 读取一个 800 GB 的 XML 文件,并使用 etree 的迭代解析器来解析它。
目前,我只是用 open('foo.txt')
来打开文件,没有使用任何缓冲参数。我有点困惑,不知道这样做是否合适,还是应该使用缓冲参数,或者用一些来自 io 的东西,比如 io.BufferedReader、io.open 或 io.TextIOBase。
如果能给我一些正确的方向建议,我会非常感激。
3 个回答
我没有尝试过处理那么大的xml文件,但上次我处理一些大(而且相对简单)的xml文件时,我用了一个叫做sax解析器的工具。
这个工具基本上会在每发生一个“事件”时给你一个回调,让你自己决定怎么保存需要的数据。你可以直接给它一个打开的文件,这样就不需要一次性把整个文件读进来。
你试过懒加载函数吗?可以看看这个链接:在Python中读取大文件的懒加载方法。
这个方法似乎已经能解决你的问题了。不过,我建议你可以考虑用这个方法把数据写入数据库,mysql是免费的:http://dev.mysql.com/downloads/。另外,NoSQL也是免费的,可能更适合处理像800GB这样的大量数据:http://www.oracle.com/technetwork/database/nosqldb/downloads/default-495311.html。
标准的 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 的文件时,磁盘缓存几乎会被耗尽。