Разбор сжатого 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 очищается для каждого элемента тега.