Разбор XML с помощью minidom

Я борюсь с минидомом. Мне нужно найти запись в dom, обновить содержащийся в ней текст, а затем сохранить файл. До сих пор единственный способ, которым мне удалось найти конкретный элемент, - это очень явный, прямой, жестко закодированный метод:

doc.childNodes[0].childNodes[3].childNodes[5].childNodes[11].childNodes[1].childNodes[3] 

Я просто хочу обновить первый <text> в <typeBox type="counter">.

<typeBoxes>
    <typeBox type="counter">
        <text fontSize="140">123456</text>
        <text fontSize="26">Foobar</text>
        <incrementTextFieldNum>1</incrementTextFieldNum>
        <timing>1</timing>
        <increment>1</increment>
    </typeBox>
    <typeBox>
        <image>images/foo.png</image>
        <text fontSize="26">Foo</text>-->
    </typeBox>

    ...

Предложения?


person Brian D    schedule 22.07.2011    source источник
comment
Что вы можете найти в обычном режиме? Можете ли вы найти элемент typeBox? Можете ли вы получить список элементов text? Покажи нам код того, как далеко ты продвинулся.   -  person agf    schedule 23.07.2011
comment
Ну, я смог получить к нему немного лучший доступ с помощью doc.getElementsByTagName('typeBox')[0].childNodes[1], но даже это все еще жестко закодированные индексы.   -  person Brian D    schedule 23.07.2011


Ответы (2)


minidom не очень мощный, большинство людей в наши дни используют некоторые варианты ElementTree. В Python 2.5 и более поздних версиях он встроен.

>>> from xml.etree import ElementTree as etree
>>> corpus = """<typeBoxes>
...     <typeBox type="counter">
...         <text fontSize="140">123456</text>
...         <text fontSize="26">Foobar</text>
...         <incrementTextFieldNum>1</incrementTextFieldNum>
...         <timing>1</timing>
...         <increment>1</increment>
...     </typeBox>
...     <typeBox>
...         <image>images/foo.png</image>
...         <text fontSize="26">Foo</text>-->
...     </typeBox>
... </typeBoxes>"""
>>> 
>>> doc = etree.fromstring(corpus)
>>> 
>>> for typeBox in doc.findall('typeBox'):
...     if typeBox.attrib.get('type') == 'counter':
...         fieldnum = int(typeBox.find('incrementTextFieldNum').text)
...         incr = int(typeBox.find('increment').text)
...         text_field = typeBox.findall('text')[fieldnum-1]
...         text_field.text = str(int(text_field.text) + incr)
... 
>>> print etree.tostring(doc)
<typeBoxes>
    <typeBox type="counter">
        <text fontSize="140">123457</text>
        <text fontSize="26">Foobar</text>
        <incrementTextFieldNum>1</incrementTextFieldNum>
        <timing>1</timing>
        <increment>1</increment>
    </typeBox>
    <typeBox>
        <image>images/foo.png</image>
        <text fontSize="26">Foo</text>--&gt;
    </typeBox>
</typeBoxes>
>>> 
person SingleNegationElimination    schedule 22.07.2011
comment
Интересно, что doc.findall('typeBox') ничего не находит - person Brian D; 23.07.2011
comment
действительно ли корневой узел документа, который вы анализируете, является <typeBoxes>? если это что-то другое, вам может потребоваться сначала перейти к этому узлу. - person SingleNegationElimination; 23.07.2011
comment
Нет, это ‹stories› ‹wall› ‹wallBoxes› ‹typeBoxes›. - person Brian D; 23.07.2011
comment
Ах да, ››› e.getroot() ‹Элемент 'typeBoxes' по адресу 0x101d00910› ››› e.findall('typeBox') [‹Элемент 'typeBox' по адресу 0x101d00950›, ‹Элемент 'typeBox' по адресу 0x101d00b10›, ‹ Элемент 'typeBox' по адресу 0x101d00b90›, ‹Элемент 'typeBox' по адресу 0x101d00c10›, ‹Элемент 'typeBox' по адресу 0x101d00c90›, ‹Элемент 'typeBox' по адресу 0x101d00d50›] - person Brian D; 23.07.2011
comment
Нет проблем сделать то же самое с минидомом. - person agf; 23.07.2011

Если вы хотите найти первый элемент в списке childNodes, попробуйте использовать:

typeBox_node = next((node for node in typeBoxs_node.childNodes \
                              if node.localName == 'typeBox'))

Если вы хотите сделать это для каждого элемента, попробуйте:

for typeBox_node in doc.getElementsByTagName('typeBox'):
    text_node = next((node for node in typeBox_node.childNodes \
                              if node.localName == 'text'))
person TorelTwiddler    schedule 22.07.2011