【问题标题】:How can I parse an XML document into a Python object?如何将 XML 文档解析为 Python 对象?
【发布时间】:2015-09-12 06:02:58
【问题描述】:

我正在尝试使用 XML API。我想要一些代表 XML 数据的 Python 对象。我有几个 XSD 和文档中的一些示例 API 响应。

这是一个 XML 响应示例:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<serial:serialHeaderType xmlns:isan="http://www.isan.org/ISAN/isan"
                         xmlns:title="http://www.isan.org/schema/v1.11/common/title"
                         xmlns:serial="http://www.isan.org/schema/v1.21/common/serial"
                         xmlns:externalid="http://www.isan.org/schema/v1.11/common/externalid"
                         xmlns:common="http://www.isan.org/schema/v1.11/common/common"
                         xmlns:participant="http://www.isan.org/schema/v1.11/common/participant"
                         xmlns:language="http://www.isan.org/schema/v1.11/common/language"
                         xmlns:country="http://www.isan.org/schema/v1.11/common/country">
    <common:status>
        <common:DataType>SERIAL_HEADER_TYPE</common:DataType>
        <common:ISAN root="0000-0002-3B9F"/>
        <common:WorkStatus>ACTIVE</common:WorkStatus>
    </common:status>
    <serial:SerialHeaderId root="0000-0002-3B9F"/>
    <serial:MainTitles>
        <title:TitleDetail>
            <title:Title>Braquo</title:Title>
            <title:Language>
                <language:LanguageLabel>French</language:LanguageLabel>
                <language:LanguageCode>
                    <language:CodingSystem>ISO639_2</language:CodingSystem>
                    <language:ISO639_2Code>FRE</language:ISO639_2Code>
                </language:LanguageCode>
            </title:Language>
            <title:TitleKind>ORIGINAL</title:TitleKind>
        </title:TitleDetail>
    </serial:MainTitles>
    <serial:TotalEpisodes>11</serial:TotalEpisodes>
    <serial:TotalSeasons>0</serial:TotalSeasons>
    <serial:MinDuration>
        <common:TimeUnit>MIN</common:TimeUnit>
        <common:TimeValue>45</common:TimeValue>
    </serial:MinDuration>
    <serial:MaxDuration>
        <common:TimeUnit>MIN</common:TimeUnit>
        <common:TimeValue>144</common:TimeValue>
    </serial:MaxDuration>
    <serial:MinYear>2009</serial:MinYear>
    <serial:MaxYear>2009</serial:MaxYear>
    <serial:MainParticipantList>
        <participant:Participant>
            <participant:FirstName>Frédéric</participant:FirstName>
            <participant:LastName>Schoendoerffer</participant:LastName>
            <participant:RoleCode>DIR</participant:RoleCode>
        </participant:Participant>
        <participant:Participant>
            <participant:FirstName>Karole</participant:FirstName>
            <participant:LastName>Rocher</participant:LastName>
            <participant:RoleCode>ACT</participant:RoleCode>
        </participant:Participant>
    </serial:MainParticipantList>
    <serial:CompanyList>
        <common:Company>
            <common:CompanyKind>PRO</common:CompanyKind>
            <common:CompanyName>R.T.B.F.</common:CompanyName>
        </common:Company>
        <common:Company>
            <common:CompanyKind>PRO</common:CompanyKind>
            <common:CompanyName>Capa Drama</common:CompanyName>
        </common:Company>
        <common:Company>
            <common:CompanyKind>PRO</common:CompanyKind>
            <common:CompanyName>Marathon</common:CompanyName>
        </common:Company>
    </serial:CompanyList>
</serial:serialHeaderType>

我尝试简单地忽略 XSD 并在从 API 获得的 XML 上使用 lxml.objectify。我遇到了命名空间的问题。必须使用其显式命名空间来引用每个子节点是一件非常痛苦的事情,并且不利于代码的可读性。

from lxml import objectify
obj = objectify.fromstring(response)
print obj.MainTitles.TitleDetail
# This will fail to find the element because you need to specify the namespace
print obj.MainTitles['{http://www.isan.org/schema/v1.11/common/title}TitleDetail']
# Or something like that, I couldn't get it to work, and I'd much rather use attributes and not specify the namespace

然后我尝试generateDS 为我创建一些 Python 类定义。我丢失了这次尝试给我的错误消息,但我无法让它工作。它会为我给它的每个 XSD 生成一个模块,但它不会解析示例 XML。

我现在正在尝试pyxb,到目前为止这似乎好多了。它生成的定义比 generateDS 更好(将它们拆分为多个可重用的模块),但它不会解析 XML:

from models import serial
obj = serial.CreateFromDocument(response)

Traceback (most recent call last):
  ...
  File "/vagrant/isan/isan.py", line 58, in lookup
    return serial.CreateFromDocument(resp.content)
  File "/vagrant/isan/models/serial.py", line 69, in CreateFromDocument
    instance = handler.rootObject()
  File "/home/vagrant/venv/lib/python2.7/site-packages/pyxb/binding/saxer.py", line 285, in rootObject
    raise pyxb.UnrecognizedDOMRootNodeError(self.__rootObject)
UnrecognizedDOMRootNodeError: <pyxb.utils.saxdom.Element object at 0x2b53664dc850>

无法识别的节点是示例中的&lt;serial:serialHeaderType&gt; 节点。查看pyxb 源,似乎这个错误来自“如果顶级元素被处理为 DOM 实例”,但我不知道这意味着什么或如何防止它。

尝试探索这个我已经筋疲力尽了,我不知道下一步该做什么。

【问题讨论】:

    标签: python xml xsd lxml pyxb


    【解决方案1】:

    我很幸运地使用 Beautiful Soup 将 XML 解析为 Python。它非常简单,并且它们提供了非常强大的文档。在这里查看: http://www.crummy.com/software/BeautifulSoup/ http://www.crummy.com/software/BeautifulSoup/bs4/doc/

    【讨论】:

    • 我以前用过 BeautifulSoup(但用于 HTML)。我可能会试一试。在它如何通过 HTML 处理 XML 之间是否有任何需要注意的问题?
    • 老实说这很简单,不需要太多代码。这是我发现的一个 XML 示例,以了解基本概念:stackoverflow.com/questions/7785831/…
    • 我玩了很短的时间,但我走不远。 bs4 不理解命名空间(看起来完全不理解),文档中没有关于它们的任何内容。这意味着我不能像soup.MainTitles 那样使用普通的对象访问,我必须使用soup.find('serial:MainTitles')。这比 lxml 选项略好,但并不多:(
    • 你能具体说明你想从 xml 中解析出什么吗? (特别是您需要从中提取哪些标题),我可以为您提供一个简单的示例。
    【解决方案2】:

    UnrecognizedDOMRootNodeError 表示 PyXB 无法在已注册绑定的命名空间中找到该元素。在您的情况下,它在第一个元素上失败,即 {http://www.isan.org/schema/v1.21/common/serial}serialHeaderType。

    schema for that namespace 定义了一个名为 SerialHeaderType 的 complexType,但没有定义一个名为 serialHeaderType 的元素。事实上,它没有定义顶级元素。所以 PyXB 无法识别,XML 不验证。

    您需要找到用于提供元素的命名空间的附加架构,或者您发送的消息确实无法验证。这可能是因为有人期望从复杂类型到具有该类型的元素的隐式映射,或者因为它是通常会在 QName 是成员元素名称的其他元素中找到的片段。

    更新:您可以通过添加 按照serial.py中生成的绑定:

    serialHeaderType = pyxb.binding.basis.element(pyxb.namespace.ExpandedName(Namespace, 'serialHeaderType'), SerialHeaderType)
    Namespace.addCategoryObject('elementBinding', serialHeaderType.name().localName(), serialHeaderType)
    

    如果你这样做,你不会得到 UnrecognizedDOMRootNodeError 但你 将在以下位置获得IncompleteElementContentError:

    <common:status>
        <common:DataType>SERIAL_HEADER_TYPE</common:DataType>
        <common:ISAN root="0000-0002-3B9F"/>
        <common:WorkStatus>ACTIVE</common:WorkStatus>
    </common:status>
    

    提供以下详细信息:

    The containing element {http://www.isan.org/schema/v1.11/common/common}status is defined at common.xsd[243:3].
    The containing element type {http://www.isan.org/schema/v1.11/common/common}StatusType is defined at common.xsd[289:1]
    The {http://www.isan.org/schema/v1.11/common/common}StatusType automaton is not in an accepting state.
    Any accepted content has been stored in instance
    The following element and wildcard content would be accepted:
        An element {http://www.isan.org/schema/v1.11/common/common}ActiveISAN per common.xsd[316:3]
        An element {http://www.isan.org/schema/v1.11/common/common}MatchingISANs per common.xsd[317:3]
        An element {http://www.isan.org/schema/v1.11/common/common}Description per common.xsd[318:3]
    No content remains unconsumed
    

    查看架构可确认至少缺少 {http://www.isan.org/schema/v1.11/common/common}Description 元素但这是必需的。

    因此,这些文档似乎不应该被验证,而 PyXB 是 可能使用了错误的技术。

    【讨论】:

    • 感谢 pabigot,这是一个令人沮丧的情况,但至少我不会再浪费时间尝试验证了!
    猜你喜欢
    • 1970-01-01
    • 2011-07-28
    • 2014-07-30
    • 1970-01-01
    • 2012-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    相关资源
    最近更新 更多