LXML Xpath似乎没有返回完整路径

时间:2013-07-09 21:25:24

标签: python xpath xml-parsing lxml

好的我会是第一个承认它的人,只是不是我想要的路径而且我不知道如何得到它。

我在Eclipse中使用Python 3.3,在Windows 7中使用Pydev插件,在家中使用ubuntu 13.04。我是python的新手,并且编程经验有限。

我正在尝试编写一个脚本来接收XML Lloyds市场保险消息,找到所有标签并将它们转储到.csv中我们可以轻松更新它们,然后重新导入它们以创建更新的xml。

我已经成功完成了所有这些工作,除非我获得所有标签,它只提供标签名称,而不是上面的标签。

<TechAccount Sender="broker" Receiver="insurer">
<UUId>2EF40080-F618-4FF7-833C-A34EA6A57B73</UUId>
<BrokerReference>HOY123/456</BrokerReference>
<ServiceProviderReference>2012080921401A1</ServiceProviderReference>
<CreationDate>2012-08-10</CreationDate>
<AccountTransactionType>premium</AccountTransactionType>
<GroupReference>2012080921401A1</GroupReference>
<ItemsInGroupTotal>
<Count>1</Count>
</ItemsInGroupTotal>
<ServiceProviderGroupReference>8-2012-08-10</ServiceProviderGroupReference>
<ServiceProviderGroupItemsTotal>
<Count>13</Count>
</ServiceProviderGroupItemsTotal>

这是XML的一个片段。我想要的是找到所有标签及其路径。例如,我想将它显示为ItemsInGroupTotal / Count,但只能将其显示为Count。

这是我的代码:

xml = etree.parse(fullpath)
print( xml.xpath('.//*'))
all_xpath = xml.xpath('.//*')
every_tag = []
for i in all_xpath:
    single_tag = '%s,%s' % (i.tag, i.text)
    every_tag.append(single_tag)
print(every_tag)

这给出了:

'{http://www.ACORD.org/standards/Jv-Ins-Reinsurance/1}ServiceProviderGroupReference,8-2012-08-10', '{http://www.ACORD.org/standards/Jv-Ins-Reinsurance/1}ServiceProviderGroupItemsTotal,\n', '{http://www.ACORD.org/standards/Jv-Ins-Reinsurance/1}Count,13',

正如您所见,Count显示为{namespace} Count,13而不是{namespace} ItemsInGroupTotal / Count,13

有人能指出我需要的东西吗?

谢谢(希望我的第一篇文章没问题)

亚当

编辑:

这是我现在的代码:     使用open(fullpath,'rb')作为xmlFilepath:        xmlfile = xmlFilepath.read()

fulltext = '%s' % xmlfile
text = fulltext[2:]
print(text)


xml = etree.fromstring(fulltext)
tree = etree.ElementTree(xml)

every_tag = ['%s, %s' % (tree.getpath(e), e.text) for e in xml.iter()]
print(every_tag)

但这会返回错误: ValueError:不支持带编码声明的Unicode字符串。请使用字节输入或XML片段而不声明。

我删除前两个字符,因为你是b'并且它抱怨它没有以标签开头

更新

我一直在玩这个,如果我删除xis:xxx标签和顶部的命名空间内容,它按预期工作。我需要保留xis标签并能够将它们标识为xis标签,因此不能只删除它们。

有关如何实现这一目标的任何帮助?

2 个答案:

答案 0 :(得分:2)

  

ElementTree对象有一个方法getpath(element),它返回一个   用于查找该元素的结构,绝对XPath表达式

iter()循环中的每个元素上调用getpath应该对您有用:

from pprint import pprint
from lxml import etree


text = """
<TechAccount Sender="broker" Receiver="insurer">
    <UUId>2EF40080-F618-4FF7-833C-A34EA6A57B73</UUId>
    <BrokerReference>HOY123/456</BrokerReference>
    <ServiceProviderReference>2012080921401A1</ServiceProviderReference>
    <CreationDate>2012-08-10</CreationDate>
    <AccountTransactionType>premium</AccountTransactionType>
    <GroupReference>2012080921401A1</GroupReference>
    <ItemsInGroupTotal>
        <Count>1</Count>
    </ItemsInGroupTotal>
    <ServiceProviderGroupReference>8-2012-08-10</ServiceProviderGroupReference>
    <ServiceProviderGroupItemsTotal>
        <Count>13</Count>
    </ServiceProviderGroupItemsTotal>
</TechAccount>
"""

xml = etree.fromstring(text)
tree = etree.ElementTree(xml)

every_tag = ['%s, %s' % (tree.getpath(e), e.text) for e in xml.iter()]
pprint(every_tag)

打印:

['/TechAccount, \n',
 '/TechAccount/UUId, 2EF40080-F618-4FF7-833C-A34EA6A57B73',
 '/TechAccount/BrokerReference, HOY123/456',
 '/TechAccount/ServiceProviderReference, 2012080921401A1',
 '/TechAccount/CreationDate, 2012-08-10',
 '/TechAccount/AccountTransactionType, premium',
 '/TechAccount/GroupReference, 2012080921401A1',
 '/TechAccount/ItemsInGroupTotal, \n',
 '/TechAccount/ItemsInGroupTotal/Count, 1',
 '/TechAccount/ServiceProviderGroupReference, 8-2012-08-10',
 '/TechAccount/ServiceProviderGroupItemsTotal, \n',
 '/TechAccount/ServiceProviderGroupItemsTotal/Count, 13']

UPD: 如果您的xml数据位于文件test.xml中,则代码如下所示:

from pprint import pprint
from lxml import etree

xml = etree.parse('test.xml').getroot()
tree = etree.ElementTree(xml)

every_tag = ['%s, %s' % (tree.getpath(e), e.text) for e in xml.iter()]
pprint(every_tag)

希望有所帮助。

答案 1 :(得分:2)

getpath()确实会返回一个不适合人类消费的xpath。从这个xpath,你可以建立一个更有用的。比如这种快速而肮脏的方法:

def human_xpath(element):
    full_xpath = element.getroottree().getpath(element)
    xpath = ''
    human_xpath = ''
    for i, node in enumerate(full_xpath.split('/')[1:]):
        xpath += '/' + node
        element = element.xpath(xpath)[0]
        namespace, tag = element.tag[1:].split('}', 1)
        if element.getparent() is not None:
            nsmap = {'ns': namespace}
            same_name = element.getparent().xpath('./ns:' + tag,
                                                  namespaces=nsmap)
            if len(same_name) > 1:
                tag += '[{}]'.format(same_name.index(element) + 1)
        human_xpath += '/' + tag
    return human_xpath