XML-анализ с помощью Python и minidom
Я использую Python (minidom) для анализа XML файла, который печатает иерархическую структуру, которая выглядит примерно так (здесь используется отступ, чтобы показать значительную иерархическую взаимосвязь):
My Document
Overview
Basic Features
About This Software
Platforms Supported
Вместо этого программа повторяется несколько раз над узлами и производит следующее, дублируя узлы. (Рассматривая список node на каждой итерации, очевидно, почему он это делает, но я не могу найти способ получить список node, который я ищу.)
My Document
Overview
Basic Features
About This Software
Platforms Supported
Basic Features
About This Software
Platforms Supported
Platforms Supported
Вот исходный XML файл:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
Вот программа Python:
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("test.xml")
Topic=dom.getElementsByTagName('Topic')
i = 0
for node in Topic:
alist=node.getElementsByTagName('Title')
for a in alist:
Title= a.firstChild.data
print Title
Я мог бы решить проблему, не вложив элементы "Тема" , изменив имена тем нижнего уровня на что-то вроде "SubTopic1" и "SubTopic2". Но я хочу использовать встроенное XML-иерархическое структурирование без необходимости использования разных имен элементов; кажется, что я должен иметь возможность встраивать элементы "Тема" и что должен быть какой-то способ узнать, на каком уровне "Тема" я сейчас смотрю.
Я пробовал несколько различных функций XPath без особого успеха.
Ответы
Ответ 1
getElementsByTagName рекурсивно, вы получите все потомки с соответствующим tagName. Поскольку ваши Темы содержат другие Темы, которые также имеют Заголовки, вызов будет получать заголовки с понижающим титром много раз.
Если вы хотите запросить только для всех соответствующих прямых детей, и у вас нет доступного XPath, вы можете написать простой фильтр, например:
def getChildrenByTagName(node, tagName):
for child in node.childNodes:
if child.nodeType==child.ELEMENT_NODE and (tagName=='*' or child.tagName==tagName):
yield child
for topic in document.getElementsByTagName('Topic'):
title= list(getChildrenByTagName('Title'))[0] # or just get(...).next()
print title.firstChild.data
Ответ 2
Позвольте мне поставить этот комментарий здесь...
Спасибо за попытку. Это не сработало, но это дало мне несколько идей. Следующие работы (одна и та же общая идея: FWIW, nodeType - ELEMENT_NODE):
import xml.dom.minidom
from xml.dom.minidom import Node
dom = xml.dom.minidom.parse("docmap.xml")
def getChildrenByTitle(node):
for child in node.childNodes:
if child.localName=='Title':
yield child
Topic=dom.getElementsByTagName('Topic')
for node in Topic:
alist=getChildrenByTitle(node)
for a in alist:
# Title= a.firstChild.data
Title= a.childNodes[0].nodeValue
print Title
Ответ 3
Вы можете использовать следующий генератор для запуска списка и получения заголовков с уровнями отступов:
def f(elem, level=-1):
if elem.nodeName == "Title":
yield elem.childNodes[0].nodeValue, level
elif elem.nodeType == elem.ELEMENT_NODE:
for child in elem.childNodes:
for e, l in f(child, level + 1):
yield e, l
Если вы проверите его с вашим файлом:
import xml.dom.minidom as minidom
doc = minidom.parse("test.xml")
list(f(doc))
вы получите список со следующими кортежами:
(u'My Document', 1),
(u'Overview', 1),
(u'Basic Features', 2),
(u'About This Software', 2),
(u'Platforms Supported', 3)
Разумеется, это всего лишь базовая идея. Если вы просто хотите использовать пробелы в начале, вы можете запрограммировать это непосредственно в генераторе, хотя с уровнем, которым вы обладаете большей гибкостью. Вы также можете автоматически обнаружить первый уровень (здесь это всего лишь плохая работа по инициализации уровня до -1...).
Ответ 4
Рекультивирующая функция:
import xml.dom.minidom
def traverseTree(document, depth=0):
tag = document.tagName
for child in document.childNodes:
if child.nodeType == child.TEXT_NODE:
if document.tagName == 'Title':
print depth*' ', child.data
if child.nodeType == xml.dom.Node.ELEMENT_NODE:
traverseTree(child, depth+1)
filename = 'sample.xml'
dom = xml.dom.minidom.parse(filename)
traverseTree(dom.documentElement)
Ваш xml:
<?xml version="1.0" encoding="UTF-8"?>
<DOCMAP>
<Topic Target="ALL">
<Title>My Document</Title>
</Topic>
<Topic Target="ALL">
<Title>Overview</Title>
<Topic Target="ALL">
<Title>Basic Features</Title>
</Topic>
<Topic Target="ALL">
<Title>About This Software</Title>
<Topic Target="ALL">
<Title>Platforms Supported</Title>
</Topic>
</Topic>
</Topic>
</DOCMAP>
Ваш желаемый результат:
$ python parse_sample.py
My Document
Overview
Basic Features
About This Software
Platforms Supported
Ответ 5
Я думаю, что может помочь
import os
import sys
import subprocess
import base64,xml.dom.minidom
from xml.dom.minidom import Node
f = open("file.xml",'r')
data = f.read()
i = 0
doc = xml.dom.minidom.parseString(data)
for topic in doc.getElementsByTagName('Topic'):
title= doc.getElementsByTagName('Title')[i].firstChild.nodeValue
print title
i +=1
Вывод:
My Document
Overview
Basic Features
About This Software
Platforms Supported