【问题标题】:LXML Xpath does not seem to return full pathLXML Xpath 似乎没有返回完整路径
【发布时间】:2013-07-07 16:02:01
【问题描述】:

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

我在 Eclipse 中使用 Python 3.3,在工作中的 Windows 7 和家里的 ubuntu 13.04 中都使用 Pydev 插件。我是 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 标签,所以不能直接删除它们。

对我如何实现这一点有任何帮助吗?

【问题讨论】:

    标签: python xpath xml-parsing lxml


    【解决方案1】:

    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']
    

    更新: 如果您的 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)
    

    希望对您有所帮助。

    【讨论】:

    • 非常感谢,但我无法让它为我工作。我从文件中读取 XML,而不是直接将其放入文本中,并且尝试将其转换为字符串似乎失败了。实现这一目标的任何提示?
    • 当然,将etree.fromstring(text) 替换为etree.parse(file_name)
    • 抱歉应该说我试过了,结果:TypeError: Argument 'element' has wrong type (expected lxml.etree._Element, got lxml.etree._ElementTree)
    • 感谢您的快速回复,您更新的代码对我有用,但输出:'/*, \n', '/*/*, \n', '/*/*/*[1 ], 2EF40080-F618-4FF7-833C-A34EA6A57B73', '/*/*/*[2], HOY123/456', '/*/*/*[3], etc. 这是否指向xml不存在以某种方式格式化以获得我想要的路径?有没有办法发布整个 xml?它有 177 行长,我应该将整个内容粘贴到问题中吗?
    • 是的,getpath 无法处理带有命名空间的复杂 xml。
    【解决方案2】:

    getpath() 确实返回了一个不适合人类使用的 xpath。不过,您可以从这个 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
    

    【讨论】:

    • 这很有趣。我正在寻找类似的问题。我打开了一个问题,如何浏览和列出 XML 消息的 XPATH? stackoverflow.com/questions/29173143/… 如何将human_xpath python 函数插入到我发布的代码中?感谢您的指导。
    猜你喜欢
    • 2018-05-16
    • 2022-01-04
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多