Эффективный способ разбора XML в ElementTree (1.3.0) Python - PullRequest
5 голосов
/ 25 сентября 2011

Я пытаюсь проанализировать огромный XML-файл в диапазоне (20 МБ-3 ГБ).Файлы представляют собой образцы из разных инструментов.Итак, что я делаю, так это нахожу необходимую информацию об элементе из файла и вставляю ее в базу данных (Django).

Небольшая часть моего образца файла.Пространство имен существует во всех файлах.Интересная особенность файлов в том, что они имеют больше атрибутов узла, чем текст

<?xml VERSION="1.0" encoding="ISO-8859-1"?>
<mzML xmlns="http://psi.hupo.org/ms/mzml" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0.xsd" accession="plgs_example" version="1.1.0" id="urn:lsid:proteios.org:mzml.plgs_example">

    <instrumentConfiguration id="QTOF">
                    <cvParam cvRef="MS" accession="MS:1000189" name="Q-Tof ultima"/>
                    <componentList count="4">
                            <source order="1">
                                    <cvParam cvRef="MS" accession="MS:1000398" name="nanoelectrospray"/>
                            </source>
                            <analyzer order="2">
                                    <cvParam cvRef="MS" accession="MS:1000081" name="quadrupole"/>
                            </analyzer>
                            <analyzer order="3">
                                    <cvParam cvRef="MS" accession="MS:1000084" name="time-of-flight"/>
                            </analyzer>
                            <detector order="4">
                                    <cvParam cvRef="MS" accession="MS:1000114" name="microchannel plate detector"/>
                            </detector>
                    </componentList>
     </instrumentConfiguration>

Маленький, но полный файл здесь

Так что до сих пор я использовал findall для каждого интересующего элемента.

import xml.etree.ElementTree as ET
tree=ET.parse('plgs_example.mzML')
root=tree.getroot()
NS="{http://psi.hupo.org/ms/mzml}"
s=tree.findall('.//{http://psi.hupo.org/ms/mzml}instrumentConfiguration')
for ins in range(len(s)):
    insattrib=s[ins].attrib
    # It will print out all the id attribute of instrument
    print insattrib["id"] 

Как получить доступ ко всем дочерним элементам / внукам элемента instrumentConfiguration (s)?

s=tree.findall('.//{http://psi.hupo.org/ms/mzml}instrumentConfiguration')

Пример того, что я хочу

InstrumentConfiguration
-----------------------
Id:QTOF
Parameter1: T-Tof ultima
source:nanoelectrospray
analyzer: quadrupole
analyzer: time-of-flight
detector: microchannel plate decector

Существует ли эффективный способ синтаксического анализа элемента / подэлемента / подэлемента, когда существует пространство имен?Или мне нужно каждый раз использовать find / findall для доступа к конкретному элементу дерева с пространством имен?Это лишь небольшой пример, который я должен проанализировать более сложную иерархию элементов.

Любые предложения!

Редактировать

Не получен правильный ответ, поэтому необходимо изменить еще раз!

Ответы [ 5 ]

4 голосов
/ 30 октября 2011

Вот скрипт, который анализирует один миллион <instrumentConfiguration/> элементов (967MB файл) за 40 секунды (на моем компьютере) без использования большого объема памяти.

Пропускная способность 24MB/s.cElementTree page (2005) сообщает 47MB/s.

#!/usr/bin/env python
from itertools import imap, islice, izip
from operator  import itemgetter
from xml.etree import cElementTree as etree

def parsexml(filename):
    it = imap(itemgetter(1),
              iter(etree.iterparse(filename, events=('start',))))
    root = next(it) # get root element
    for elem in it:
        if elem.tag == '{http://psi.hupo.org/ms/mzml}instrumentConfiguration':
            values = [('Id', elem.get('id')),
                      ('Parameter1', next(it).get('name'))] # cvParam
            componentList_count = int(next(it).get('count'))
            for parent, child in islice(izip(it, it), componentList_count):
                key = parent.tag.partition('}')[2]
                value = child.get('name')
                assert child.tag.endswith('cvParam')
                values.append((key, value))
            yield values
            root.clear() # preserve memory

def print_values(it):
    for line in (': '.join(val) for conf in it for val in conf):
        print(line)

print_values(parsexml(filename))

Вывод

$ /usr/bin/time python parse_mxml.py
Id: QTOF
Parameter1: Q-Tof ultima
source: nanoelectrospray
analyzer: quadrupole
analyzer: time-of-flight
detector: microchannel plate detector
38.51user 1.16system 0:40.09elapsed 98%CPU (0avgtext+0avgdata 23360maxresident)k
1984784inputs+0outputs (2major+1634minor)pagefaults 0swaps

Примечание: Код хрупок предполагает, чтопервые два потомка <instrumentConfiguration/> - это <cvParam/> и <componentList/>, и все значения доступны в виде имен или атрибутов тегов.

При производительности

ElementTree 1.3 примерно в 6 раз медленнее, чем cElementTree 1.0.6 в этом случае.

Если вы замените root.clear() на elem.clear(), тогда код будет на ~ 10% быстрее, но в ~ 10 раз больше памяти.lxml.etree работает с вариантом elem.clear(), производительность такая же, как и для cElementTree, но он потребляет в 20 (root.clear()) / 2 (elem.clear()) раз больше памяти (500 МБ).

3 голосов
/ 06 марта 2012

Если это все еще актуальная проблема, вы можете попробовать pymzML, интерфейс python для файлов mzMLСайт: http://pymzml.github.com/

0 голосов
/ 14 ноября 2014

Я знаю, что это старая проблема, но я сталкиваюсь с этой проблемой при анализе XML, где мои файлы XML действительно большие.

Ответ Ф.Ф. Себастьяна действительно правильный, но возникла следующая проблема.

Что я заметил, так это то, что иногда значения в elem.text (если у вас есть значения внутри XML, а не как атрибуты) не читаются правильно (иногда возвращается None), если вы выполняете итерацию по начальным атрибутам.Я должен был пройти через «конец» следующим образом

it = imap(itemgetter(1),
          iter(etree.iterparse(filename, events=('end',))))
root = next(it) # get root element

Если кто-то хочет получить текст внутри тега xml (а не атрибута), возможно, ему следует выполнить итерацию событий «конец», а не «start '.

Однако, если все значения находятся в атрибутах, то код в ответе Дж. Ф. Себастьяна является более правильным.

Пример XML для моего случая:

<data>
<country>
    <name>Liechtenstein</name>
    <rank>1</rank>
    <year>2008</year>
    <gdppc>141100</gdppc>
</country>
<country>
    <name>Singapore</name>
    <rank>4</rank>
    <year>2011</year>
    <gdppc>59900</gdppc>
</country>
<country>
    <name>Panama</name>
    <rank>68</rank>
    <year>2011</year>
    <gdppc>13600</gdppc>
</country>

0 голосов
/ 26 сентября 2011

Если ваши файлы огромны, взгляните на функцию iterparse().Обязательно прочитайте эту статью автора elementtree, особенно часть о "инкрементальном разборе".

0 голосов
/ 25 сентября 2011

В этом случае я бы нашел, чтобы найти все элементы instrumentList. Затем для этих результатов просто получите доступ к данным, как будто бы инструментальный список и инструмент были массивами, вы получите все элементы и вам не придется искать их все.

...