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

时间:2015-04-01 21:21:12

标签: 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.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]

Geofabrick extracts完美配合。但是,当我尝试使用相同的脚本解析planet-latest.osm.bz2时,我得到错误:

  

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

以下是我尝试过的事情:

  • 检查planet-latest.osm.bz2 md5sum
  • 检查带有bz2的脚本停止的planet-latest.osm。没有明显错误,该属性称为“num_changes”,而不是错误中指示的“num_change”
  • 我也做了一些愚蠢的事,但错误让我困惑:我在'rb'模式中打开了planet -osm.bz2 [c = BZ2File('file.osm.bz2','rb')]然后将c.read()传递给iterparse(),它返回一个错误说(非常长的字符串)无法打开。奇怪的是,(非常长的字符串)在“规范授权值”错误引用的地方结束......

然后我尝试首先使用简单的

解压缩planet.osm.gz2
bzcat planet.osm.gz2 > planet.osm

直接在planet.osm上运行解析器。而且......它奏效了!我对此感到非常困惑,并且找不到任何可能发生这种情况的指针以及如何解决这个问题。我的猜测是解压缩和解析之间会发生一些事情,但我不确定。请帮我理解!

2 个答案:

答案 0 :(得分:5)

事实证明,问题在于压缩的planet.osm文件。

OSM Wiki所示,行星文件被压缩为多流文件,而bz2 python模块无法读取多流文件。但是,bz2文档指出了可以读取此类文件的替代模块bz2file。我用过它,它完美无缺!

所以代码应该是:

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格式的研究(正如评论中所建议的),我偶然发现了imposm.parser,这是一个python模块,它实现了OSM数据的通用解析器(pbf或xml格式)。你可能想看看这个!

答案 1 :(得分:2)

作为替代方案,您可以使用bzcat命令的输出(也可以处理多流文件):

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