【问题标题】:Python and libxml2: how to iterate in xml nodes with XPATHPython 和 libxml2:如何使用 XPATH 迭代 xml 节点
【发布时间】:2010-07-29 19:00:48
【问题描述】:

我在从 XML 树中检索信息时遇到问题。

我的 XML 有这个形状:

<?xml version="1.0"?>
<records xmlns="http://www.mysyte.com/foo">
  <record>
    <id>first</id>
    <name>john</name>
    <papers>
      <paper>john_1</paper>
      <paper>john_2</paper>
    </papers>
  </record>
  <record>
    <id>second</id>
    <name>mike</name>
    <papers>
      <paper>mike_a</paper>
      <paper>mike_b</paper>
    </papers>
  </record>
  <record>
    <id>third</id>
    <name>albert</name>
    <papers>
      <paper>paper of al</paper>
      <paper>other paper</paper>
    </papers>
  </record>
</records>

我想要做的是提取如下数据元组:

[{'code': 'first', 'name': 'john'}, 
 {'code': 'second', 'name': 'mike'}, 
 {'code': 'third', 'name': 'albert'}]

现在我写了这个 python 代码:

try:
  doc = libxml2.parseDoc(xml)
except (libxml2.parserError, TypeError):
  print "Problems loading XML"

ctxt = doc.xpathNewContext()
ctxt.xpathRegisterNs("pre", "http://www.mysyte.com/foo")

record_nodes = ctxt.xpathEval('/pre:records/pre:record')

for record_node in record_nodes:
  id = record_node.xpathEval('id')[0].content
  name = record_node.xpathEval('name')[0].content
  ret_list.append({'code': id, 'name': name})

我的问题是我没有任何结果,而且当我在节点上迭代时,我的印象是我对 XPATH 做错了。

我还尝试使用这些 XPATH 获取 id 和名称:

/id
/name
/record/id
/record/name
/pre:id
/pre:name

等等,但有任何结果(顺便说一句,如果我在子查询中使用前缀,我会出错)。

有什么想法吗?

【问题讨论】:

    标签: python xml xpath libxml2


    【解决方案1】:

    这是一个建议。注意setContextNode() 方法:

    import libxml2
    
    xml = "test.xml"
    doc = libxml2.parseFile(xml) 
    
    ctxt = doc.xpathNewContext() 
    ctxt.xpathRegisterNs("pre","http://www.mysyte.com/foo") 
    
    ret_list = []
    record_nodes = ctxt.xpathEval('/pre:records/pre:record') 
    
    for node in record_nodes:
        ctxt.setContextNode(node)
        _id = ctxt.xpathEval('pre:id')[0].content
        name = ctxt.xpathEval('pre:name')[0].content
        ret_list.append({'code': _id, 'name': name}) 
    
    print ret_list
    

    【讨论】:

    • 对不起!我忘了签这个答案是最好的!它实际上以我想要的方式工作。谢谢!
    【解决方案2】:

    您可以使用单个 XPath 表达式选择所有需要的元素

    /pre:records/pre:record/*[self::pre:id or self::pre:name]
    

    然后在python中处理选定的节点。

    【讨论】:

    • 抱歉,这不能回答我的问题
    • @Giovanni-Di-Milia:这回答了 XPath 部分——我不懂 Python。选择了所需的所有节点后,您应该能够在 Python 中处理它们并产生所需的结果。
    • 这是否保证节点返回的任何顺序?如果不是,这会在 python 端增加一些复杂性,以便跟踪哪个id 属于哪个name
    • @Andre-Holzner:我知道的所有 XPath 引擎都按文档顺序返回所选模式。而 libxml 也不例外。
    【解决方案3】:

    如果可以切换到lxml,这是一种可行的方法:

    import lxml.etree as le
    root=le.XML(content)
    result=[]
    namespaces={'pre':'http://www.mysyte.com/foo'}
    for record in root:
        id=record.xpath('pre:id',namespaces=namespaces)[0]
        name=record.xpath('pre:name',namespaces=namespaces)[0]
        result.append({'code':id.text,'name':name.text})
    print(result)
    # [{'code': 'first', 'name': 'john'}, {'code': 'second', 'name': 'mike'}, {'code': 'third', 'name': 'albert'}]
    

    Dimitre Novatchev's XPath expression 为基础,您可以这样做:

    id_name_nodes = iter(ctxt.xpathEval('/pre:records/pre:record/*[self::pre:id or self::pre:name]'))
    
    ret_list=[]
    for id,name in zip(id_name_nodes,id_name_nodes):
        ret_list.append({'code':id.content,'name':name.content})
    print(ret_list)
    

    这个 libxml2 代码依赖于每个具有 id 和名称的记录。 如果idname 丢失,ret_list 将配对错误的 id 和 name,静默失败。在同样的情况下,lxml 代码会报错。

    【讨论】:

    • 我到处都在使用 libxml2,在这种情况下我也想继续使用它。不过谢谢你的回答!
    • lxml 也使用libxml2 库(&libxslt)。它基本上是最上面的一层,可以让这类棘手的事情变得简单。
    【解决方案4】:

    libxslt 由于某种原因缺少如此重要的命名空间支持,但我们可以预先解析 xml 文件,从中预先读取命名空间,然后使用这些命名空间调用 xsltproc

    def xpath(xml, xpathexpression):
        f=open(xml)
        fcontent = f.read()
        f.close()
    
        doc=libxml2.parseFile(xml)
        xp = doc.xpathNewContext()
        for nsdeclaration in re.findall('xmlns:*\w*="[^"]*"', fcontent):
            m = re.match('xmlns:(\w+)=.*', nsdeclaration)
            if m:
                ns = m.group(1)
            else:
                ns = "default"
            url = nsdeclaration[nsdeclaration.find('"')+1:nsdeclaration.rfind('"')]
            xp.xpathRegisterNs(ns, url)
        a=xp.xpathEval(xpathexpression)
        if len(a):
            return a[0].content
        return ""
    

    【讨论】:

    • 我认为这并不能回答问题或为已经写好的内容添加更多内容
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-03
    • 1970-01-01
    • 2011-06-01
    • 2020-08-10
    • 1970-01-01
    相关资源
    最近更新 更多