关于python:将压缩的xml提要解析为ElementTree

Parsing compressed xml feed into ElementTree

我正在尝试将以下提要解析为python中的elementtree:"http://smarkets.s3.amazonaws.com/oddsfeed.xml"(警告大型文件)

以下是我迄今为止所做的尝试:

1
2
3
4
5
6
7
8
9
10
11
12
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(),无限可能??(我知道这是一个大文件,但与我解析的其他非压缩源相比似乎太长了,而且这个大文件首先会扼杀gzip压缩带来的任何带宽收益)。

接下来,我用

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

但是现在

1
tree=ET.parse(r.content)

1
tree=ET.parse(r.text)

但这也引发了例外。

正确的方法是什么?


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

1
2
3
4
5
6
7
8
9
10
#!/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()允许解析不适合内存的文件。

1
2
3
4
5
6
7
8
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树。


ET.parse函数接受"包含XML数据的文件名或文件对象"。您给它一个充满XML的字符串。它将尝试打开一个文件,该文件的名称就是这一大块XML。可能没有这样的文件。

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

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

这些都在文档中的简短教程中介绍:

We can import this data by reading from a file:

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

Or directly from a string:

1
root = ET.fromstring(country_data_as_string)