我能够使用 lxml 来完成我想做的大部分事情,尽管通过这些令人困惑的示例和教程是一件很困难的事情。简而言之,我能够读取外部 xml 文件并通过 lxml 将其导入适当的树状格式。
为了证明这一点,如果我输入:
print(etree.tostring(myXmlTree, pretty_print= True, method= "xml") )
我得到以下输出:
<net xmlns="http://www.arin.net/whoisrws/core/v1" xmlns:ns2="http://www.arin.net/whoisrws/rdns/v1" xmlns:ns3="http://www.arin.net/whoisrws/netref/v2" termsOfUse="https://www.arin.net/whois_tou.html">
<registrationDate>2006-08-29T00:00:00-04:00</registrationDate>
<ref>http://whois.arin.net/rest/net/NET-79-0-0-0-1</ref>
<endAddress>79.255.255.255</endAddress>
<handle>NET-79-0-0-0-1</handle>
<name>79-RIPE</name>
<netBlocks>
<netBlock>
<cidrLength>8</cidrLength>
<endAddress>79.255.255.255</endAddress>
<description>Allocated to RIPE NCC</description>
<type>RN</type>
<startAddress>79.0.0.0</startAddress>
</netBlock>
</netBlocks>
<orgRef name="RIPE Network Coordination Centre" handle="RIPE">http://whois.arin.net/rest/org/RIPE</orgRef>
<comment>
<line number="0">These addresses have been further assigned to users in</line>
<line number="1">the RIPE NCC region. Contact information can be found in</line>
<line number="2">the RIPE database at http://www.ripe.net/whois</line>
</comment>
<startAddress>79.0.0.0</startAddress>
<updateDate>2009-05-18T07:34:02-04:00</updateDate>
<version>4</version>
</net>
好吧,这对人类消费很好,但对机器没有用。如果我想要特定的元素,比如 xml 中的开始和结束 IP 地址,我可以键入:
ns = myXmlTree.nsmap.values()[0]
myXmlTree.findall("{" + ns + "}startAddress")[0].text
myXmlTree.findall("{" + ns + "}endAddress")[0].text
我会收到:
'79.0.0.0'
'79.255.255.255'
但我仍然需要以人的身份查看 xml 文件,以了解其中包含哪些元素。相反,我希望能够检索特定级别的所有元素的名称,然后自动遍历该级别。因此,例如,我想做类似的事情:
myElements = myXmlTree.findallelements("{" + ns + "}")
它会给我一个类似这样的返回值:
['registrationDate', 'ref', 'endAddress', 'handle', 'name', 'netBlocks', 'orgRef', 'comment', 'startAddress', 'updateDate', 'version']
如果它能告诉我元素的整个结构,包括嵌套元素,那就太棒了。
我确定有办法,否则就没有意义。
提前致谢!
P.S.,我知道我可以迭代并遍历所有迭代的列表。我希望 lxml 中已经有一个包含这些数据的方法。如果迭代是唯一的方法,我想那没关系……我觉得这很笨拙。
最佳答案
我相信您正在寻找 element.xpath()
.
XPath不是 lxml
引入的概念而是一种通用的查询语言,用于从许多处理 XML 的事物支持的 XML 文档中选择节点。将其视为类似于 CSS 选择器的东西,但更强大(也更复杂一点)。参见 XPath Syntax .
您的文档使用了命名空间——我暂时忽略它并在文章末尾解释如何处理它们,因为这样可以使示例更具可读性。 (但它们不会按原样用于您的文档)。
例如,
tree.xpath('/net/endAddress')
将选择 <endAddress>79.255.255.255</endAddress>
<net />
正下方的元素节点。但不是 <endAddress />
在<netBlock>
里面.
XPath 表达式
tree.xpath('//endAddress')
但是会选择所有 <endAddress />
文档中任意位置的节点。
您当然可以进一步查询使用 XPath 表达式返回的节点:
netblocks = tree.xpath('/net/netBlocks/netBlock')
for netblock in netblocks:
start = netblock.xpath('./startAddress/text()')[0]
end = netblock.xpath('./endAddress/text()')[0]
print "%s - %s" % (start, end)
给你
79.0.0.0 - 79.255.255.255
注意 .xpath()
总是返回选定节点的列表 - 所以如果你只想要一个,请考虑到这一点。
您还可以通过属性选择元素:
comment = tree.xpath('/net/comment')[0]
line_2 = comment.xpath("./line[@number='2']")[0]
这将选择 <line />
带有 number="2"
的元素从第一条评论开始。
您也可以自己选择属性:
numbers = tree.xpath('//line/attribute::number')
['0', '1', '2']
要获取您最后询问的元素名称列表,您可以这样做:
names = [node.tag for node in tree.xpath('/net/*')]
['registrationDate', 'ref', 'endAddress', 'handle', 'name', 'netBlocks', 'orgRef', 'comment', 'startAddress', 'updateDate', 'version']
但考虑到 XPath 的强大功能,最好只查询文档以了解您想要从中了解的内容,具体或松散取决于您认为合适。
现在,命名空间。正如您所注意到的,如果您的文档使用 XML namespace ,则需要在很多地方考虑到这一点,XPath 也不异常(exception)。查询命名空间文档时,您传递 xpath()
像这样处理命名空间映射:
NSMAP = {'ns': 'http://www.arin.net/whoisrws/core/v1',
'ns2': 'http://www.arin.net/whoisrws/rdns/v1',
'ns3': 'http://www.arin.net/whoisrws/netref/v2'}
names = [node.tag for node in tree.xpath('/ns:net/*', namespaces=NSMAP)]
在 lxml
的许多其他地方您可以使用 None
指定默认 namespace 作为命名空间映射中的字典键。不适用于 xpath()
不幸的是,这会引发异常
TypeError: empty namespace prefix is not supported in XPath
所以不幸的是,您必须在 XPath 表达式中为每个节点名称添加前缀 ns:
(或者您选择将该命名空间映射到的任何内容)。
有关 XPath 语法的更多信息,请参阅 XPath Syntax 示例W3Schools Xpath Tutorial 中的页面.
要开始使用 XPath,在许多 XPath testers 中的一个中摆弄您的文档也很有帮助。 .此外,Firefox 的 Firebug 插件或 Google Chrome 检查器允许您显示所选元素的(或者更确切地说,许多之一)XPath。
关于python - 我如何在 Python 中使用 LXML 捕获 XML 文件的所有元素名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19456562/