Анализ сжатого 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)
но они вызывают исключения.
Каков правильный способ сделать это?
Ответы
Ответ 1
Функция 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)
Ответ 2
Вы можете передать значение, возвращаемое 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 очищается для каждого элемента тега.