将压缩的xml提要解析为ElementTree

时间:2014-10-18 00:00:25

标签: python xml gzip elementtree

我正在尝试将以下Feed解析为python中的ElementTree:“http://smarkets.s3.amazonaws.com/oddsfeed.xml”(警告大文件)

这是我到目前为止所尝试的内容:

feed = urllib.urlopen("http://smarkets.s3.amazonaws.com/oddsfeed.xml")

# feed is compressed
compressed_data = feed.read()
import StringIO
compressedstream = StringIO.StringIO(compressed_data)
import gzip
gzipper = gzip.GzipFile(fileobj=compressedstream)
data = gzipper.read()

# Parse XML
tree = ET.parse(data)

但似乎只是挂在compressed_data = feed.read()上,无限可能? (我知道这是一个很大的文件,但与我解析的其他非压缩的feed相比似乎太长了,而且这个大的数据首先会从gzip压缩中获得任何带宽增益)。

接下来,我尝试了requests

url = "http://smarkets.s3.amazonaws.com/oddsfeed.xml"
headers = {'accept-encoding': 'gzip, deflate'}
r = requests.get(url, headers=headers, stream=True)

但现在

tree=ET.parse(r.content)

tree=ET.parse(r.text)

但这会引发例外。

这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:5)

您可以将urlopen()返回的值直接传递给GzipFile(),然后您可以将其传递给ElementTree方法,例如iterparse()

#!/usr/bin/env python3
import xml.etree.ElementTree as etree
from gzip import GzipFile
from urllib.request import urlopen, Request

with urlopen(Request("http://smarkets.s3.amazonaws.com/oddsfeed.xml",
                     headers={"Accept-Encoding": "gzip"})) as response, \
     GzipFile(fileobj=response) as xml_file:
    for elem in getelements(xml_file, 'interesting_tag'):
        process(elem)

其中getelements()允许解析不适合内存的文件。

def getelements(filename_or_file, tag):
    """Yield *tag* elements from *filename_or_file* xml incrementaly."""
    context = iter(etree.iterparse(filename_or_file, events=('start', 'end')))
    _, root = next(context) # get root element
    for event, elem in context:
        if event == 'end' and elem.tag == tag:
            yield elem
            root.clear() # free memory

为了保留内存,在每个标记元素上清除构造的xml树。

答案 1 :(得分:2)

ET.parse函数采用“包含XML数据的文件名或文件对象”。你给它一个充满XML的字符串。它将尝试打开一个名为XML大块的文件。可能没有这样的文件。

您需要fromstring函数或XML构造函数。

或者,如果您愿意,您已经有了一个文件对象gzipper;您可以将其传递给parse,而不是将其读入字符串。


这些都由文档中的简短Tutorial涵盖:

  

我们可以通过从文件中读取来导入此数据:

import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
  

或直接来自字符串:

root = ET.fromstring(country_data_as_string)