用python中的lxml iterparse解析一个大的.bz2文件(40GB)。未与未压缩文件一起出现的错误

用python中的lxml iterparse解析一个大的.bz2文件(40GB)。未与未压缩文件一起出现的错误,python,xml,lxml,openstreetmap,Python,Xml,Lxml,Openstreetmap,我试图解析OpenStreetMap的planet.osm,它是以bz2格式压缩的。因为它已经是41G了,我不想完全解压这个文件 因此,我找到了如何使用bz2和lxml解析planet.osm文件的部分内容,使用以下代码 from lxml import etree as et from bz2 import BZ2File path = "where/my/fileis.osm.bz2" with BZ2File(path) as xml_file: parser = et.iter

我试图解析OpenStreetMap的planet.osm,它是以bz2格式压缩的。因为它已经是41G了,我不想完全解压这个文件

因此,我找到了如何使用bz2和lxml解析planet.osm文件的部分内容,使用以下代码

from lxml import etree as et
from bz2 import BZ2File

path = "where/my/fileis.osm.bz2"
with BZ2File(path) as xml_file:
    parser = et.iterparse(xml_file, events=('end',))
    for events, elem in parser:

        if elem.tag == "tag":
            continue
        if elem.tag == "node":
            (do something)


    ## Do some cleaning
    # Get rid of that element
    elem.clear()

    # Also eliminate now-empty references from the root node to node        
    while elem.getprevious() is not None:
        del elem.getparent()[0]
这与我们的产品完美地结合在一起。但是,当我试图用相同的脚本解析planet-latest.osm.bz2时,我得到了错误:

xml.etree.XMLSyntaxError:属性num_change的规范强制值,第3684行,第60列

以下是我尝试过的东西:

  • 检查planet-latest.osm.bz2 md5sum
  • 检查planet-latest.osm,其中包含bz2的脚本停止。没有明显的错误,该属性称为“num\u changes”,而不是错误中指示的“num\u change”
  • 我也做了一些愚蠢的事情,但这个错误让我困惑:我在模式“rb”[c=BZ2File('file.osm.bz2','rb')]下打开了planet-latest.osm.bz2,然后将c.read()传递给iterparse(),这返回了一个错误,说(很长的字符串)无法打开。奇怪的是,(很长的字符串)正好在“规格指令值”错误所指的地方结束
然后我试着先用一个简单的方法解压planet.osm.gz2

bzcat planet.osm.gz2 > planet.osm

并直接在planet.osm上运行解析器。而且。。。成功了!我对此感到非常困惑,并且找不到任何关于为什么会发生这种情况以及如何解决这种情况的指针。我猜在解压和解析之间会发生一些事情,但我不确定。请帮我理解

原来问题出在压缩的planet.osm文件上

如上所示,planet文件被压缩为多流文件,bz2 python模块无法读取多流文件。但是,bz2文档指出了可以读取此类文件的替代模块。我用过它,它工作得很好

因此,代码应为:

from lxml import etree as et
from bz2file import BZ2File

path = "where/my/fileis.osm.bz2"
with BZ2File(path) as xml_file:
    parser = et.iterparse(xml_file, events=('end',))
    for events, elem in parser:

        if elem.tag == "tag":
            continue
        if elem.tag == "node":
            (do something)


    ## Do some cleaning
    # Get rid of that element
    elem.clear()

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

另外,在研究使用PBF格式(如评论中所建议的)时,我偶然发现了一个python模块,它实现了OSM数据的通用解析器(PBF或xml格式)。你可能想看看这个

您也可以使用
bzcat
命令的输出(该命令也可以处理多流文件):


当然,不能确定,但是
BZ2File(file.osm.bz2,'rb')
看起来不正确,因为根据文档,第一个参数被假定为文件名(即字符串)。bz2模块中可能有一个bug(我怀疑维护人员经常对40GB输入进行测试)。尝试编写一个Python脚本,使用bz2模块解压数据并将其写入新文件,并验证输出是否与
bzcat
输出匹配。谢谢@AndrewMedico,您确认了我的猜测。我做了一些研究,最终在bz2 bug追踪器上找到了这个。这让我明白了一句话:planet.osm.bz2不能用python的BZ2File读取,因为转储是多流Bzip2文件,而BZ2File目前不支持这些文件。OSMwiki指出了我将要尝试的解决方法。我会张贴的结果作为一个答案,如果它的工作!与bz2没有直接关系,但我只想指出,您可以使用下载预过滤的OSM数据,因此不必处理如此大的文件。除了@scai建议的内容外,您还可以查看PBF格式而不是OSM.bz2。它小得多,而且一般来说处理速度也更快。
p = subprocess.Popen(["bzcat", "data.bz2"], stdout=subprocess.PIPE)
parser = et.iterparse(p.stdout, ...)
# at the end just check that p.returncode == 0 so there were no errors