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