Каков наилучший способ проанализировать большой XML и создать фрейм данных с данными в XML (с помощью python или другого)?

Я пытаюсь сделать таблицу (или csv, я использую фрейм данных pandas) из информации XML-файла.

Файл находится здесь (.zip — 14 МБ, XML — ~370 МБ), https://nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.3.xml.zip . Он содержит информацию о пакетах на разных языках — node.js, python, java и т. д., также известный как список CPE 2.3, составленный правительственной организацией США NVD.

вот как это выглядит в первых 30 рядах:

<cpe-list xmlns:config = "http://scap.nist.gov/schema/configuration/0.1" xmlns = "http://cpe.mitre.org/dictionary/2.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:scap-core = "http://scap.nist.gov/schema/scap-core/0.3" xmlns:cpe-23 = "http://scap.nist.gov/schema/cpe-extension/2.3" xmlns:ns6 = "http://scap.nist.gov/schema/scap-core/0.1" xmlns:meta = "http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2" xsi:schemaLocation = "http://scap.nist.gov/schema/cpe-extension/2.3 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary-extension_2.3.xsd http://cpe.mitre.org/dictionary/2.0 https://scap.nist.gov/schema/cpe/2.3/cpe-dictionary_2.3.xsd http://scap.nist.gov/schema/cpe-dictionary-metadata/0.2 https://scap.nist.gov/schema/cpe/2.1/cpe-dictionary-metadata_0.2.xsd http://scap.nist.gov/schema/scap-core/0.3 https://scap.nist.gov/schema/nvd/scap-core_0.3.xsd http://scap.nist.gov/schema/configuration/0.1 https://scap.nist.gov/schema/nvd/configuration_0.1.xsd http://scap.nist.gov/schema/scap-core/0.1 https://scap.nist.gov/schema/nvd/scap-core_0.1.xsd">
  <generator>
    <product_name>National Vulnerability Database (NVD)</product_name>
    <product_version>4.9</product_version>
    <schema_version>2.3</schema_version>
    <timestamp>2022-03-17T03:51:01.909Z</timestamp>
  </generator>
  <cpe-item name = "cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~">
    <title xml:lang = "en-US">$0.99 Kindle Books project $0.99 Kindle Books (aka com.kindle.books.for99) for android 6.0</title>
    <references>
      <reference href = "https://play.google.com/store/apps/details?id=com.kindle.books.for99">Product information</reference>
      <reference href = "https://docs.google.com/spreadsheets/d/1t5GXwjw82SyunALVJb2w0zi3FoLRIkfGPc7AMjRF0r4/edit?pli=1#gid=1053404143">Government Advisory</reference>
    </references>
    <cpe-23:cpe23-item name = "cpe:2.3:a:\$0.99_kindle_books_project:\$0.99_kindle_books:6:*:*:*:*:android:*:*"/>
  </cpe-item>

Древовидная структура XML-файла довольно проста, корень — «cpe-list», дочерний элемент — «cpe-item», а дочерние элементы — «title», «references» и «cpe23-item».

Из «заголовка» мне нужен текст в элементе; Из «cpe23-item» мне нужен атрибут «имя»; Из «ссылок» мне нужны атрибуты «href» от его правнуков, «ссылка».

Фрейм данных должен выглядеть так:

  | cpe23_name   | title_text            | ref1  | ref2  | ref3  | ref_other
0 | 'cpe23name 1'| 'this is a python pkg'| 'url1'| 'url2'| NaN   | NaN
1 | 'cpe23name 2'| 'this is a java pkg'  | 'url1'| 'url2'| NaN   | NaN
... 

мой код здесь, законченный через ~ 100 секунд:

import xml.etree.ElementTree as et

xtree = et.parse("official-cpe-dictionary_v2.3.xml")
xroot = xtree.getroot()

import time
start_time = time.time()

df_cols = ["cpe", "text", "vendor", "product", "version", "changelog", "advisory", 'others']

title      = '{http://cpe.mitre.org/dictionary/2.0}title'
ref        = '{http://cpe.mitre.org/dictionary/2.0}references'
cpe_item   = '{http://scap.nist.gov/schema/cpe-extension/2.3}cpe23-item'

p_cpe = None
p_text = None
p_vend = None
p_prod = None
p_vers = None
p_chan = None
p_advi = None
p_othe = None
rows = []

i = 0

while i < len(xroot):
    for elm in xroot[i]:
        if elm.tag == title:
                p_text = elm.text
                #assign p_text
       
        elif elm.tag == ref:
            for nn in elm:
                s = nn.text.lower()
                #check the lower text in refs
                
                if 'version' in s:
                    p_vers = nn.attrib.get('href')
                    #assign p_vers
                elif 'advisor' in s:
                    p_advi = nn.attrib.get('href')
                    #assign p_advi
                elif 'product' in s:
                    p_prod = nn.attrib.get('href')
                    #assign p_prod
                elif 'vendor' in s:
                    p_vend = nn.attrib.get('href')
                    #assign p_vend
                elif 'change' in s:
                    p_chan = nn.attrib.get('href')
                    #assign p_vend
                else:
                    p_othe = nn.attrib.get('href')
            
        elif elm.tag == cpe_item:
            p_cpe = elm.attrib.get("name")
            #assign p_cpe
           
        else:
            print(elm.tag)
    row = [p_cpe, p_text, p_vend, p_prod, p_vers, p_chan, p_advi, p_othe]
    rows.append(row)        
    
    p_cpe = None
    p_text = None
    p_vend = None
    p_prod = None
    p_vers = None
    p_chan = None
    p_advi = None
    p_othe = None
    print(len(rows)) #this shows how far I got during the running time
    i+=1
    
out_df1 = pd.DataFrame(rows, columns = df_cols)# move this part outside the loop by removing the indent

print("---853k rows take %s seconds ---" % (time.time() - start_time))
  • обновлено: более быстрый способ - переместить 2-й последний ряд за пределы петли. Поскольку «строки» уже получают информацию в каждом цикле, нет необходимости каждый раз создавать новый фрейм данных. время работы теперь составляет 136,0491042137146 секунд. ура!

Мой ноутбук — MacBook Air (retina 2019), двухъядерный процессор Intel I5 с тактовой частотой 1,6 ГГц, память 8 ГБ. Я попробую на своем рабочем столе и поделюсь дополнительной информацией.

Richard Fan 16.03.2022 20:51

Вместо того, чтобы описывать структуру XML, тем более, что у вас есть пространства имен, можете ли вы опубликовать фрагмент (особенно с корнем) XML и избежать загрузки большого zip-архива?

Parfait 16.03.2022 22:25
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
34
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку ваш XML довольно плоский, обратите внимание на недавно добавленный модуль ввода-вывода pandas.read_xml, представленный в версии 1.3. Данный XML использует пространство имен по умолчанию, для ссылки на элементы в xpath используйте namespaces аргумент:

url = "https://nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.3.xml.zip"

df = pd.read_xml(
    url, xpath = ".//doc:cpe-item", namespaces = {'doc': 'http://cpe.mitre.org/dictionary/2.0'}
)

Если у вас не установлен парсер по умолчанию, lxml, используйте парсер etree:

df = pd.read_xml(
    url, xpath = ".//doc:cpe-item", namespaces = {'doc': 'http://cpe.mitre.org/dictionary/2.0'}, parser = "etree"
)

Другие вопросы по теме