Разбор сжатого XML-канала в ElementTree

Я пытаюсь проанализировать следующий канал в ElementTree в python: " 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()бесконечно может быть?? (Я знаю, что это большой файл, но он кажется слишком длинным по сравнению с другими несжатыми фидами, которые я проанализировал, и этот большой размер убивает любой прирост пропускной способности от сжатия 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 ответа

Решение

ET.parse Функция принимает "имя файла или файловый объект, содержащий данные XML". Вы даете ему строку, полную XML. Он попытается открыть файл, имя которого - большой кусок XML. Вероятно, нет такого файла.

Вы хотите fromstring функция или XML конструктор.

Или, если вы предпочитаете, у вас уже есть объект файла, gzipper; Вы могли бы просто передать это parse вместо того, чтобы читать это в строку.


Это все описано в кратком руководстве в документации:

Мы можем импортировать эти данные, читая из файла:

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

Или прямо из строки:

root = ET.fromstring(country_data_as_string)

Вы можете передать значение, возвращаемое 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 очищается для каждого элемента тега.

Другие вопросы по тегам