Что я пробовал: Я бы предпочел не использовать SAX, потому что он требует большого количества запутанно структурированного шаблонного кода.
Способ iterparse (как из lxml.etree, так и из xml.etree .ElementTree) — это Как обсуждалось, это часто звучит так, как будто XML-файлы анализируются «итеративно», например, построчно.
Но оказывается, что на практике, если в качестве входных данных используется файлоподобный объект, оба парсера ищут и анализируют выходные данные метода .read этого файлового объекта. (В отличие от вывода .readline, как я ожидал.) Если этот файлоподобный объект является файловым указателем на файл размером 8 ГБ и он выполняется на узле кластера с памятью 2 ГБ, это, конечно, будет вызвать ошибку OOM.
Этот вывод основан на ответе на связанный вопрос, а также на моем собственном тестировании. Вот MWE:
Код: Выделить всё
import io
from lxml import etree
import xml.etree.ElementTree as etree2
xml_string = """
Manager
Star Team Member
"""
#### lxml output
for event, element in etree.iterparse(io.BytesIO(xml_string.encode("UTF-8")), recover=True, remove_blank_text=True,
events=("start", "end",)):
print(str((event, element, element.tag,
element.text.strip() if element.text is not None else element.text,
element.tail.strip() if element.tail is not None else element.tail)) + "\n")
print(f"{etree.tostring(element)}\n")
### xml.etree.ElementTree output is the same
for event, element in etree2.iterparse(io.BytesIO(xml_string.encode("UTF-8")),
events=("start", "end",)):
print(str((event, element, element.tag,
element.text.strip() if element.text is not None else element.text,
element.tail.strip() if element.tail is not None else element.tail)) + "\n")
print(f"{etree2.tostring(element)}\n")
Я смог прийти используйте следующий обходной путь, который отображает ожидаемое построчно поведение синтаксического анализа. Однако мне интересно, есть ли лучшие решения. Например, не вызовут ли миллионы вызовов readline, которые необходимо выполнить для файла размером ~8 ГБ, узкое место или ограничение ввода-вывода?
Код: Выделить всё
### for the MWE
class StreamString(object):
def __init__(self, string):
self._io = io.StringIO(string)
def read(self, len=None):
return self._io.readline().encode("UTF-8")
def close(self):
self._io.close()
### closer to what would be used in practice
class StreamFile(object):
def __init__(self, path):
self._file = open(path, "r")
def read(self, len=None):
return self._file.readline().encode("UTF-8")
def close(self):
self._file.close()
### demonstrating the expected line-by-line parsing behavior
iterator = etree.iterparse(StreamString(xml_string), recover=True, remove_blank_text=True,
events=("start", "end",))
event, root = next(iterator)
print(str((event, root, root.tag,
root.text.strip() if root.text is not None else root.text,
root.tail.strip() if root.tail is not None else root.tail)) + "\n")
print(f"{etree.tostring(root)}\n")
for event, element in iterator:
print(str((event, element, element.tag,
element.text.strip() if element.text is not None else element.text,
element.tail.strip() if element.tail is not None else element.tail)) + "\n")
print(f"{etree.tostring(root)}\n")
Примечание: хотя строка XML, используемая для MWE, небольшая и легко помещается полностью в память, конечная цель — запустить эту для XML-файлов, размер которых потенциально составляет гигабайты, на узлах кластера с 1–2 ГБ памяти. (У меня нет контроля над вычислительной средой, да, я согласен, что было бы разумнее просто масштабировать вертикально до одного узла с памятью ~64 ГБ.)
Подробнее здесь: https://stackoverflow.com/questions/793 ... hout-using