Разбор XML с Python (найти теги с конкретным текстом) - PullRequest
0 голосов
/ 07 февраля 2019

Мне было поручено обработать XML-файл, найти определенные элементы и экспортировать их в CSV-файл ..

У меня есть определенные проблемы с информацией, хранящейся в тех же тегах:

<name>text</name>
<value>value</value>

каждый тег имени содержит свое значение, и мне нужны только некоторые из них .. Я попытался перебрать файл с таким кодом:

try:
        descr = member.find('.//name').text
        if descr == 'description':
            plugin.append(descr)
    except AttributeError:
        descr = 'Unknown'
        plugin.append(descr)

Но он возвращает только «Неизвестно»

Весь мой код как таковой (не закончен):

import xml.etree.ElementTree as ET
import csv

tree = ET.parse('plugins.xml')
root = tree.getroot()

nessus_out = open('/home/rj/Documents/python/nessus_out.csv', 'w')

csvwriter = csv.writer(nessus_out)

for member in root.findall('nasl'):
    plugin = []

    id = member.find('script_id').text
    plugin.append(id)

    name = member.find('script_name').text
    plugin.append(name)

    family = member.find('script_family').text
    plugin.append(family)

    #for each in member.iterfind('nasl'):
    try:
        solution = member.xpath('.//name/text()')
        if solution == 'solution':
            plugin.append(solution)
    except AttributeError:
        solution = 'Unknown'
        plugin.append(solution)
    csvwriter.writerow(plugin)
nessus_out.close()

Конечная цель - найти «решение» и получить соответствующее значение из его тега.

Структура xml выглядит следующим образом:

nasl_plugins
nasl_plugins/nasl
nasl_plugins/nasl/filename
nasl_plugins/nasl/script_id
nasl_plugins/nasl/script_name
nasl_plugins/nasl/script_family
nasl_plugins/nasl/attributes/attribute/name
nasl_plugins/nasl/attributes/attribute/value

Для Даниеля:

Фрагмент XML:

<nasl>
<filename>fedora_2017-c3149b5fcb.nasl</filename>
<script_id>101028</script_id>
<script_name>Fedora 25 : xen (2017-c3149b5fcb)</script_name>
<script_version>$Revision: 1.5 $</script_version>
<script_copyright>This script is Copyright (C) 2017-2018 Tenable Network Security, Inc.</script_copyright>
<script_family>Fedora Local Security Checks</script_family>
<cves>
 <cve>CVE-2017-10911</cve>
 <cve>CVE-2017-10912</cve>
 <cve>CVE-2017-10913</cve>
 <cve>CVE-2017-10915</cve>
 <cve>CVE-2017-10916</cve>
 <cve>CVE-2017-10917</cve>
 <cve>CVE-2017-10918</cve>
 <cve>CVE-2017-10919</cve>
 <cve>CVE-2017-10920</cve>
 <cve>CVE-2017-10923</cve>
</cves>
<bids>
</bids>
<xrefs>
 <xref>FEDORA:2017-c3149b5fcb</xref>
 <xref>IAVB:2017-B-0074</xref>
</xrefs>
<dependencies>
 <dependency>ssh_get_info.nasl</dependency>
</dependencies>
<required_keys>
 <key>Host/local_checks_enabled</key>
 <key>Host/RedHat/release</key>
 <key>Host/RedHat/rpm-list</key>
</required_keys>
<attribute> 
  <name>plugin_type</name> 
  <value>local</value> 
</attribute> 
<attribute> 
  <name>plugin_modification_date</name> 
  <value>2018/02/02</value> 
</attribute> 
<attribute> 
  <name>stig_severity</name> 
  <value>I</value> 
</attribute> 
<attribute> 
  <name>cvss_base_score</name> 
  <value>10.0</value> 
</attribute> 
</attributes> 

Что мне нужно, так это значения stig_severity, base_cvss_score иSom другие, а также .. Так что мои рассуждения о том, чтобы искать, и они движутся вниз на одну строку и получить значение .. Что касается CSV, мне нужно в одной строке пр.плагин, поэтому в этом формате: id,name,family,solution,description,synopsis,base_cvss_score,plugin_type,stig_severity и затем значения для следующего плагина на следующей строке ..

1 Ответ

0 голосов
/ 08 февраля 2019

Похоже, что некоторые значения, которые вы хотите, являются прямыми потомками nasl, а некоторые находятся в attributes/attribute.

То, что вы могли бы сделать, это иметь два списка (или кортежей));один с точными именами элементов и один с точными именами атрибутов (attribute/name).

Примечание: это может показаться немного запутанным, потому что в этом случае «имя атрибута» действительно является элементом с именем «атрибут» сдочерний элемент с именем «name», а не истинный атрибут XML с именем «name».

Объединение этих кортежей даст вам все поля в вашем CSV.Вы можете использовать это для создания dict, содержащего все поля со значениями по умолчанию Unknown.

Затем вы можете выполнить итерации по обоим кортежам для создания двух разных типов XPath.Если элемент существует, текстовое значение обновляется в dict.В противном случае значение остается Unknown.

Пример ...

Ввод XML (test.xml)

<nasl_plugins>
    <nasl>
        <filename>fedora_2017-c3149b5fcb.nasl</filename>
        <script_id>101028</script_id>
        <script_name>Fedora 25 : xen (2017-c3149b5fcb)</script_name>
        <script_version>$Revision: 1.5 $</script_version>
        <script_copyright>This script is Copyright (C) 2017-2018 Tenable Network Security,
            Inc.</script_copyright>
        <script_family>Fedora Local Security Checks</script_family>
        <cves>
            <cve>CVE-2017-10911</cve>
            <cve>CVE-2017-10912</cve>
            <cve>CVE-2017-10913</cve>
            <cve>CVE-2017-10915</cve>
            <cve>CVE-2017-10916</cve>
            <cve>CVE-2017-10917</cve>
            <cve>CVE-2017-10918</cve>
            <cve>CVE-2017-10919</cve>
            <cve>CVE-2017-10920</cve>
            <cve>CVE-2017-10923</cve>
        </cves>
        <bids> </bids>
        <xrefs>
            <xref>FEDORA:2017-c3149b5fcb</xref>
            <xref>IAVB:2017-B-0074</xref>
        </xrefs>
        <dependencies>
            <dependency>ssh_get_info.nasl</dependency>
        </dependencies>
        <required_keys>
            <key>Host/local_checks_enabled</key>
            <key>Host/RedHat/release</key>
            <key>Host/RedHat/rpm-list</key>
        </required_keys>
        <attributes>
            <attribute>
                <name>plugin_type</name>
                <value>local</value>
            </attribute>
            <attribute>
                <name>plugin_modification_date</name>
                <value>2018/02/02</value>
            </attribute>
            <attribute>
                <name>stig_severity</name>
                <value>I</value>
            </attribute>
            <attribute>
                <name>cvss_base_score</name>
                <value>10.0</value>
            </attribute>
        </attributes>
    </nasl>
</nasl_plugins>

Python3.x

import csv
from lxml import etree

elem_names = ('script_id', 'script_name', 'script_family')
attr_names = ('solution', 'description', 'synopsis', 'cvss_base_score', 'plugin_type',
              'stig_severity')
field_names = elem_names + attr_names

with open('test.csv', 'w', newline='', encoding='utf8') as xml_data_to_csv:

    csv_writer = csv.DictWriter(xml_data_to_csv, fieldnames=field_names, 
                                quoting=csv.QUOTE_ALL)

    csv_writer.writeheader()

    tree = etree.parse('test.xml')

    for nasl in tree.xpath('.//nasl'):
        # Build a dict containing all of the "field_names" with default values of "Unknown".
        values = {key: 'Unknown' for key in field_names}

        # Process the direct children of "nasl".
        for elem_name in elem_names:
            for child in nasl.xpath(f'*[self::{elem_name}]'):
                values[child.tag] = child.text

        # Process attribute with matching attribute names.
        for attr_name in attr_names:
            for val in nasl.xpath(f'attributes/attribute[name="{attr_name}"]/value'):
                values[attr_name] = val.text

        csv_writer.writerow(values)

Выход (test.csv)

"script_id","script_name","script_family","solution","description","synopsis","cvss_base_score","plugin_type","stig_severity"
"101028","Fedora 25 : xen (2017-c3149b5fcb)","Fedora Local Security Checks","Unknown","Unknown","Unknown","10.0","local","I"
...