Python и libxml2: как выполнять итерации в узлах xml с XPATH - PullRequest
2 голосов
/ 29 июля 2010

У меня проблема с получением информации из дерева 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
/name
/record/id
/record/name
/pre:id
/pre:name

и т. д., но с любым результатом (кстати, если я используюпрефикс в подзапросах у меня есть ошибка).

Есть идеи?

Ответы [ 4 ]

6 голосов
/ 01 августа 2010

Вот предложение.Обратите внимание на метод 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
0 голосов
/ 17 августа 2011

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 ""
0 голосов
/ 30 июля 2010

Вы можете выбрать все необходимые элементы с помощью одного выражения XPath:

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

Затем просто обработайте выбранные узлы в python.

0 голосов
/ 29 июля 2010

Если возможно переключиться на 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'}]

Построение из XPath-выражения Дмитрия Новатчева , вы можете сделать это:

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 или name, ret_list будет сопряжено с неверным идентификатором и именем, что приведет к сбою. При тех же обстоятельствах код lxml вызовет ошибку.

...