Используя lxml и iterparse() для синтаксического анализа большого (+ - 1Gb) XML файла
Мне нужно разобрать XML файл 1Gb со структурой, такой как ниже, и извлечь текст в тегах "Автор" и "Контент":
<Database>
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
[...]
<BlogPost>
<Date>MM/DD/YY</Date>
<Author>Last Name, Name</Author>
<Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
</BlogPost>
</Database>
До сих пор я пробовал две вещи: i) чтение всего файла и переход через него с помощью .find(xmltag) и ii) анализ XML файла с помощью lxml и iterparse().
Первый вариант у меня есть, чтобы работать, но он очень медленный. Второй вариант, который мне не удалось получить с земли.
Вот часть того, что у меня есть:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
if element.tag == "BlogPost":
print element.text
else:
print 'Finished'
Результатом этого является только пустое пространство без текста.
Я должен делать что-то неправильно, но я не могу этого понять. Кроме того, если это было недостаточно очевидно, я довольно новичок в python, и это первый раз, когда я использую lxml. Пожалуйста, помогите!
Ответы
Ответ 1
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print child.tag, child.text
element.clear()
окончательная очистка не позволит вам использовать слишком много памяти.
[update:], чтобы получить "все между... как строку", я думаю, вы хотите один из:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print etree.tostring(element)
element.close()
или
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print ''.join([etree.tostring(child) for child in element])
element.close()
или, возможно, даже:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
print ''.join([child.text for child in element])
element.close()
Ответ 2
Для будущих поисковиков: главный ответ здесь предлагает очистить элемент на каждой итерации, но это все равно оставляет вас с постоянно увеличивающимся набором пустых элементов, которые будут медленно накапливаться в памяти:
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for child in element:
print child.tag, child.text
element.clear()
^ Это не масштабируемое решение, особенно если ваш исходный файл становится все больше и больше. Лучшее решение - получить корневой элемент и очистить его каждый раз, когда вы загружаете полную запись. Это позволит сохранить память довольно стабильной (суб-20MB я бы сказал).
Вот решение, которое не требует поиска определенного тега. Эта функция возвращает генератор, который дает все первые дочерние узлы (например, <BlogPost>
) под корнем node (например, <Database>
). Это делается путем записи начала первого тега после корня node, затем ожидания соответствующего конечного тега, получения всего элемента, а затем очистки корня node.
from lxml import etree
xmlfile = '/path/to/xml/file.xml'
def iterate_xml(xmlfile):
doc = etree.iterparse(xmlfile, events=('start', 'end'))
_, root = next(doc)
start_tag = None
for event, element in doc:
if event == 'start' and start_tag is None:
start_tag = element.tag
if event == 'end' and element.tag == start_tag:
yield element
start_tag = None
root.clear()
Ответ 3
Я предпочитаю XPath для таких вещей:
In [1]: from lxml.etree import parse
In [2]: tree = parse('/tmp/database.xml')
In [3]: for post in tree.xpath('/Database/BlogPost'):
...: print 'Author:', post.xpath('Author')[0].text
...: print 'Content:', post.xpath('Content')[0].text
...:
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Я не уверен, что он отличается от обработки больших файлов. Комментарии об этом будут оценены.
Выполняя свой путь,
for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
for info in element.iter():
if info.tag in ('Author', 'Content'):
print info.tag, ':', info.text