如何选择当前节点的属性?

4 投票
3 回答
6415 浏览
提问于 2025-04-17 07:51

我在用Python和lxml处理XML文件。经过查询和筛选,我找到了想要的节点,但遇到了一些问题。我想通过XPath获取某个属性的值,下面是我的输入示例。

>print(etree.tostring(node, pretty_print=True ))
<rdf:li xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"  rdf:resource="urn:miriam:obo.chebi:CHEBI%3A37671"/>

我想要的值在resource=...里。目前我只是用lxml来获取这个值。我想知道是否可以仅用XPath来做到这一点?谢谢。

补充说明:我忘了说,这不是根节点,所以我不能在这里使用//。在我的XML文件中还有大约2000到3000个其他节点。我第一次尝试是用“.@attrib”和“self::*@”来试,但这些似乎都不管用。

补充说明2:我会尽量解释清楚(其实这是我第一次用XPath处理XML问题,而且英语也不是我擅长的领域……)。这是我的输入片段 http://pastebin.com/kZmVdbQQ(完整的可以从这里找到 http://www.comp-sys-bio.org/yeastnet/,使用的是第4版)。

在我的代码中,我试图获取speciesTypes节点,链接是chebi(<rdf:li rdf:resource="urn:miriam:obo.chebi:...."/>))。然后我想从rdf:li中的rdf:resource属性获取值。问题是,我很确定如果从父节点speciesTypes开始,获取子节点的属性会很简单,但我想知道如果从rdf:li开始该怎么做。根据我的理解,XPath中的“//”会从任何地方查找节点,而不仅仅是在当前节点中。

下面是我的代码

import lxml.etree as etree

tree = etree.parse("yeast_4.02.xml")
root = tree.getroot()
ns = {"sbml": "http://www.sbml.org/sbml/level2/version4", 
      "rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
      "body":"http://www.w3.org/1999/xhtml",
      "re": "http://exslt.org/regular-expressions"
      }
#good enough for now
maybemeta = root.xpath("//sbml:speciesType[descendant::rdf:li[starts-with(@rdf:resource, 'urn:miriam:obo.chebi') and not(starts-with(@rdf:resource, 'urn:miriam:uniprot'))]]", namespaces = ns)

def extract_name_and_chebi(node):
    name = node.attrib['name']
    chebies = node.xpath("./sbml:annotation//rdf:li[starts-with(@rdf:resource, 'urn:miriam:obo.chebi') and not(starts-with(@rdf:resource, 'urn:miriam:uniprot'))]", namespaces=ns) #get all rdf:li node with chebi resource
    assert len(chebies) == 1
    #my current solution to get rdf:resource value from rdf:li node
    rdfNS = "{" + ns.get('rdf') + "}"
    chebi = chebies[0].attrib[rdfNS + 'resource'] 
    #do protein later
    return (name, chebi)

    metaWithChebi = map(extract_name_and_chebi, maybemeta)
fo = open("metabolites.txt", "w")

for name, chebi in metaWithChebi:
    fo.write("{0}\t{1}\n".format(name, chebi))

3 个回答

0

好的,我明白了。我这里需要的xpath表达式是"./@rdf:resource",而不是".@rdf:resource"。但为什么呢?我以为"./"是指当前节点的子节点。

1

要从当前节点选择名为 rdf:resource 的属性,可以使用这个XPath表达式

@rdf:resource

为了让这个操作“正常工作”,你必须将前缀 "rdf:" 和对应的命名空间关联起来。

如果你不知道如何注册rdf命名空间,仍然可以选择这个属性 -- 使用这个XPath表达式:

@*[name()='rdf:resource']
3

在XPath查询中,把属性名前面加上@

>>> from lxml import etree
>>> xml = """\
... <?xml version="1.0" encoding="utf8"?>
... <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
...     <rdf:li rdf:resource="urn:miriam:obo.chebi:CHEBI%3A37671"/>
... </rdf:RDF>
... """
>>> tree = etree.fromstring(xml)
>>> ns = {'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
>>> tree.xpath('//rdf:li/@rdf:resource', namespaces=ns)
['urn:miriam:obo.chebi:CHEBI%3A37671']

编辑

这是问题中脚本的修改版本:

import lxml.etree as etree

ns = {
    'sbml': 'http://www.sbml.org/sbml/level2/version4',
    'rdf':'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
    'body':'http://www.w3.org/1999/xhtml',
    're': 'http://exslt.org/regular-expressions',
    }

def extract_name_and_chebi(node):
    chebies = node.xpath("""
        .//rdf:li[
        starts-with(@rdf:resource, 'urn:miriam:obo.chebi')
        ]/@rdf:resource
        """, namespaces=ns)
    return node.attrib['name'], chebies[0]

with open('yeast_4.02.xml') as xml:
    tree = etree.parse(xml)

    maybemeta = tree.xpath("""
        //sbml:speciesType[descendant::rdf:li[
        starts-with(@rdf:resource, 'urn:miriam:obo.chebi')]]
        """, namespaces = ns)

    with open('metabolites.txt', 'w') as output:
        for node in maybemeta:
            output.write('%s\t%s\n' % extract_name_and_chebi(node))

撰写回答