【问题标题】:Parsing a complicated XML file with Python使用 Python 解析复杂的 XML 文件
【发布时间】:2015-04-20 09:08:01
【问题描述】:

我正在尝试用 Python 解析一个非常丑陋的 XML 文件。我设法很好地融入其中,但在 npdoc 元素它失败了。我做错了什么?

XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<npexchange xmlns="http://www.example.com/npexchange/3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.5">
<article id="123" refType="Article">
<articleparts>
    <articlepart id="1234" refType="ArticlePart">
        <data>
            <npdoc xmlns="http://www.example.com/npdoc/2.1" version="2.1" xml:lang="sv_SE">
                <body>
                    <p>Lorem ipsum some random text here.</p>
                    <p>
                        <b>Yes this is HTML markup, and I would like to keep that.</b>
                    </p>
                </body>
                <headline>
                    <p>I am a headline</p>
                </headline>
                <leadin>
                    <p>I am some other text</p>
                </leadin>
            </npdoc>
        </data>
    </articlepart>
</articleparts>
</article>
</npexchange>

这是我目前的python代码:

from xml.etree.ElementTree import ElementTree

def parse(self):
    tree = ElementTree(file=filename)

    for item in tree.iter("article"):
        articleParts = item.find("articleparts")
        for articlepart in articleParts.iter("articlepart"):
            data = articlepart.find("data")
            npdoc = data.find("npdoc")

            id = item.get("id")
            headline = npdoc.find("headline").text
            leadIn = npdoc.find("leadin").text
            body = npdoc.find("body").text


    return articles

发生的情况是我取出了 id,但我无法访问 npdoc 元素内的字段。 npdoc 变量设置为 None。

更新: 通过使用 .find() 调用中的命名空间,设法将元素放入变量中。我如何获得价值?由于它是 HTML,因此无法正确显示 .text 属性。

【问题讨论】:

  • 预期输出是什么?
  • 那不是一个有效的 XML 文档。它没有根元素。
  • 预期结果是标题变量中的字符串&lt;p&gt;I am a headline&lt;/p&gt;,以此类推。
  • 有一个根元素,现在要编辑它。我的清洁有点激进。
  • 这是一个命名空间问题。是否可以为 http://www.example.com/npdoc/2.1 命名空间定义像 xmlns:n 这样的前缀?如果没有这样的前缀,就很难访问这个命名空间下的元素。

标签: python xml parsing xml-parsing


【解决方案1】:
nsmap = {'npdoc': 'http://www.example.com/npdoc/2.1'}
data = articlepart.find("npdoc:data", namespaces=nsmap)

...将找到您的 data 元素。不需要丑陋、不可靠的字符串处理。 (回复:“不可靠”——考虑一下这会对包含文字箭头括号的 CDATA 部分造成什么影响)。

【讨论】:

    【解决方案2】:

    这是我在 Python 3.4 中提出的。它当然不是万无一失的,但它可能会给你一些想法。

    import xml.etree.ElementTree as ET
    tree = ET.parse(r'C:\Users\Gord\Desktop\nasty.xml')
    npexchange = tree.getroot()
    for article in npexchange:
        for articleparts in article:
            for articlepart in articleparts:
                id = articlepart.attrib['id']
                print("ArticlePart - id: {0}".format(id))
                for data in articlepart:
                    for npdoc in data:
                        for child in npdoc:
                            tag = child.tag[child.tag.find('}')+1:]
                            print("    {0}:".format(tag))  ## e.g., "body:"
                            contents = ET.tostring(child).decode('utf-8')
                            contents = contents.replace('<ns0:', '<')
                            contents = contents.replace('</ns0:', '</')
                            contents = contents.replace(' xmlns:ns0="http://www.example.com/npdoc/2.1">', '>')
                            contents = contents.replace('<' + tag + '>\n', '')
                            contents = contents.replace('</' + tag + '>', '')
                            contents = contents.strip()
                            print("        {0}".format(contents))
    

    控制台输出是

    ArticlePart - id: 1234
        body:
            <p>Lorem ipsum some random text here.</p>
                                <p>
                                    <b>Yes this is HTML markup, and I would like to keep that.</b>
                                </p>
        headline:
            <p>I am a headline</p>
        leadin:
            <p>I am some other text</p>
    

    更新

    有一些改进的版本

    • 命名空间映射(由 Charles 建议),
    • register_namespace 带有一个空前缀以删除一些命名空间前缀“噪音”,以及
    • 使用.findall() 而不是盲目地遍历子节点而不管它们的标签:
    import xml.etree.ElementTree as ET
    npdoc_uri = 'http://www.example.com/npdoc/2.1'
    nsmap = {
        'npexchange': 'http://www.example.com/npexchange/3.5',
        'npdoc': npdoc_uri
        }
    ET.register_namespace("", npdoc_uri)
    tree = ET.parse(r'/home/gord/Desktop/nasty.xml')
    npexchange = tree.getroot()
    for article in npexchange.findall('npexchange:article', nsmap):
        for articleparts in article.findall('npexchange:articleparts', nsmap):
            for articlepart in articleparts.findall('npexchange:articlepart', nsmap):
                id = articlepart.attrib['id']
                print("ArticlePart - id: {0}".format(id))
                for data in articlepart.findall('npexchange:data', nsmap):
                    for npdoc in data.findall('npdoc:npdoc', nsmap):
                        for child in npdoc.getchildren():
                            tag = child.tag[child.tag.find('}')+1:]
                            print("    {0}:".format(tag))  ## e.g., "body:"
                            contents = ET.tostring(child).decode('utf-8')
                            # remove HTML block tags, e.g. <body ...> and </body>
                            contents = contents.replace('<' + tag + ' xmlns="' + npdoc_uri + '">\n', '')
                            contents = contents.replace('</' + tag + '>', '')
                            contents = contents.strip()
                            print("        {0}".format(contents))
    

    【讨论】:

    • 这看起来很有希望,必须进行一些修改,因为根目录中除了
      元素之外还有更多内容。 (以及
      元素中的更多废话)但我目前有一些工作。
    • 我有一种预感,可能还有更多元素。我考虑在每个循环中添加一个if thing.tag == 'thing': 块,但我认为它可能会使事情变得过于混乱。
    • 它似乎做的一件事是它删除了结束标签(&lt;/p&gt; 等)
    • 啊哈。 contents.replace('&lt;/ns0:', '&lt;') 更改为 contents.replace('&lt;/ns0:', '&lt;/')..
    • WTF?为什么你会在文本中往返并使用字符串替换而不是仅仅更改命名空间映射?
    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 2018-02-09
    • 1970-01-01
    相关资源
    最近更新 更多