Python xml синтаксический анализ etree найти элемент X по положению

Я пытаюсь проанализировать следующий xml, чтобы извлечь определенные данные, а затем в конечном итоге отредактировать данные по мере необходимости.

Вот xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CHECKLIST>
<VULN>
    <STIG_DATA>
        <VULN_ATTRIBUTE>Vuln_Num</VULN_ATTRIBUTE>
        <ATTRIBUTE_DATA>V-38438</ATTRIBUTE_DATA>
    </STIG_DATA>
    <STIG_DATA>
        <VULN_ATTRIBUTE>Rule_Title</VULN_ATTRIBUTE>
        <ATTRIBUTE_DATA>More text.</ATTRIBUTE_DATA>
    </STIG_DATA>
    <STIG_DATA>
        <VULN_ATTRIBUTE>Vuln_Discuss</VULN_ATTRIBUTE>
        <ATTRIBUTE_DATA>Some text here</ATTRIBUTE_DATA>
    </STIG_DATA>
    <STIG_DATA>
        <VULN_ATTRIBUTE>IA_Controls</VULN_ATTRIBUTE>
        <ATTRIBUTE_DATA></ATTRIBUTE_DATA>
    </STIG_DATA>
    <STIG_DATA>
        <VULN_ATTRIBUTE>Rule_Ver</VULN_ATTRIBUTE>
        <ATTRIBUTE_DATA>Gen000000</ATTRIBUTE_DATA>
    </STIG_DATA>
    <STATUS>NotAFinding</STATUS>
    <FINDING_DETAILS></FINDING_DETAILS>
    <COMMENTS></COMMENTS>        
    <SEVERITY_OVERRIDE></SEVERITY_OVERRIDE>
    <SEVERITY_JUSTIFICATION></SEVERITY_JUSTIFICATION>
</VULN>

The data that I'm looking to pull from this is the STATUS, COMMENTS and the ATTRIBUTE_DATA directly following VULN_ATTRIBUTE that matches == Rule_Ver. So in this example.

Я должен получить следующее: Gen000000 NotAFinding None

Что у меня есть до сих пор, так это то, что я могу легко получить статус и комментарии, но не могу понять часть ATTRIBUTE_DATA. Я могу найти первый (Vuln_Num), затем я попытался добавить индекс, но это дает ошибку «индекс списка вне диапазона».

Вот где я сейчас.

import xml.etree.ElementTree as ET
doc = ET.parse('test.ckl')
root=doc.getroot()

TagList = doc.findall("./VULN")

for curTag in TagList:
    StatusTag = curTag.find("STATUS")
    CommentTag = curTag.find("COMMENTS")
    DataTag = curTag.find("./STIG_DATA/ATTRIBUTE_DATA")
    print "GEN:[%s] Status:[%s] Comments: %s" %( DataTag.text, StatusTag.text, CommentTag.text)

Это дает следующий вывод: GEN:[V-38438] Status:[NotAFinding] Comments: None

Я хочу: GEN:[Gen000000] Status:[NotAFinding] Comments: None

Таким образом, конечная цель состоит в том, чтобы иметь возможность анализировать сотни из них и редактировать поле комментариев по мере необходимости. Я не думаю, что часть редактирования будет такой сложной, как только я получу нужный элемент.

Логически я вижу два способа сделать это. Либо перейдите к ATTRIBUTE_DATA[5] и возьмите текст, либо найдите VULN_ATTRIBUTE == Rule_Ver, затем возьмите следующий ATTRIBUTE_DATA.

Я пытался сделать это:

DataTag = curTag.find(".//STIG_DATA//ATTRIBUTE_DATA")[5] andDataTag[5].текст`

и оба дают мнеIndexError: list index out of range

Я видел, что в lxml есть get_element_by_id и xpath, но я не могу добавлять модули в эту систему, поэтому для меня это etree.

Заранее спасибо.


person user3699853    schedule 25.09.2014    source источник


Ответы (1)


Можно найти элемент по положению, но вы использовали неправильный синтаксис XPath. Любая из следующих строк должна работать:

DataTag = curTag.find("./STIG_DATA[5]/ATTRIBUTE_DATA")    # Note: 5, not 4
DataTag = curTag.findall("./STIG_DATA/ATTRIBUTE_DATA")[4] # Note: 4, not 5

Тем не менее, я настоятельно рекомендую не использовать это. Нет никакой гарантии, что Rule_Ver экземпляр STIG_DATA всегда будет пятым элементом.

Если бы вы могли изменить на lxml, то это работает:

DataTag = curTag.xpath(
    './STIG_DATA/VULN_ATTRIBUTE[text()="Rule_Ver"]/../ATTRIBUTE_DATA')[0]

Поскольку вы не можете использовать lxml, вы должны перебирать элементы STIG_DATA вручную, например так:

def GetData(curTag):
    for stig in curTag.findall('STIG_DATA'):
        if stig.find('VULN_ATTRIBUTE').text == 'Rule_Ver':
            return stig.find('ATTRIBUTE_DATA')

Вот полная программа с проверкой ошибок, добавленной в GetData():

import xml.etree.ElementTree as ET
doc = ET.parse('test.ckl')
root=doc.getroot()

TagList = doc.findall("./VULN")

def GetData(curTag):
    for stig in curTag.findall('STIG_DATA'):
        vuln = stig.find('VULN_ATTRIBUTE')
        if vuln is not None and vuln.text == 'Rule_Ver':
            data = stig.find('ATTRIBUTE_DATA')
            return data

for curTag in TagList:
    StatusTag = curTag.find("STATUS")
    CommentTag = curTag.find("COMMENTS")
    DataTag = GetData(curTag)
    print "GEN:[%s] Status:[%s] Comments: %s" %( DataTag.text, StatusTag.text, CommentTag.text)

Использованная литература:

person Robᵩ    schedule 25.09.2014
comment
Спасибо за информацию. Ваша функция GetData работает как чемпион. - person user3699853; 25.09.2014