Я пытаюсь сделать таблицу (или 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))
Вместо того, чтобы описывать структуру XML, тем более, что у вас есть пространства имен, можете ли вы опубликовать фрагмент (особенно с корнем) XML и избежать загрузки большого zip-архива?
Поскольку ваш 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"
)
Мой ноутбук — MacBook Air (retina 2019), двухъядерный процессор Intel I5 с тактовой частотой 1,6 ГГц, память 8 ГБ. Я попробую на своем рабочем столе и поделюсь дополнительной информацией.