使用beautifulsoup4进行xml解析,命名空间问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用beautifulsoup4进行xml解析,命名空间问题相关的知识,希望对你有一定的参考价值。

在使用beautifulsoup4(根据需要安装了lxml)以xml(word / document.xml)的形式解析.docx文件内容时,我遇到了一个问题。这部分来自xml:

    ...
    <a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
        <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
            <pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
    ...

成为这个:

    ...
    <graphic>
        <graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
            <pic>
    ...

即使我只是解析文件并保存它,也没有任何修改。像这样:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(open(filepath_in), 'xml')
    with open(filepath_out, "w+") as fd:
        fd.write(str(soup))

或者从python控制台解析xml。

对我来说,它看起来像这样声明的命名空间,不在根文档节点中,被解析器吃掉。

这是一个错误或功能吗?有没有办法在使用beautifulesoup4解析时保留这些?或者我需要切换到其他东西吗?

更新1:如果使用一些正则表达式和文本替换,我将这些命名空间声明添加到根document节点,然后beautifulsoup解析它就好了。但是我仍然感兴趣,如果在解析之前可以在不修改xml的情况下解决这个问题。

更新2:在使用beutifulsoup之后,我发现只在第一次出现时才解析名称空间声明。意味着如果tag声明了namespace,那么如果它的子节点具有名称空间声明,则不会对它们进行解析。下面是带有输出的代码示例来说明这一点。

来自bs4进口BeautifulSoup

xmls = []
xmls.append("""<name1:tag xmlns:name1="namespace1" xmlns:name2="namespace2">
<name2:intag>
text
</name2:intag>
</name1:tag>
""")
xmls.append("""<tag>
<name2:intag xmlns:name2="namespace2">
text
</name2:intag>
</tag>
""")
xmls.append("""<name1:tag xmlns:name1="namespace1">
<name2:intag xmlns:name2="namespace2">
text
</name2:intag>
</name1:tag>
""")
for i, xml in enumerate(xmls):
    print "============== xml {} ==============".format(i)
    soup = BeautifulSoup(xml, "xml")
    print soup

会产生输出:

============== xml 0 ==============
<?xml version="1.0" encoding="utf-8"?>
<name1:tag xmlns:name1="namespace1" xmlns:name2="namespace2">
<name2:intag>
text
</name2:intag>
</name1:tag>
============== xml 1 ==============
<?xml version="1.0" encoding="utf-8"?>
<tag>
<name2:intag xmlns:name2="namespace2">
text
</name2:intag>
</tag>
============== xml 2 ==============
<?xml version="1.0" encoding="utf-8"?>
<name1:tag xmlns:name1="namespace1">
<intag>
text
</intag>
</name1:tag>

看,如何正确解析前两个xmls,而第三个中的第二个声明被吃掉。

实际上这个问题不再涉及docx了。我的问题四处都是这样:这个行为是在beautifulsoup4中硬编码的,如果没有,那么我怎么能改变呢?

答案

从W3C建议:

Prefix提供限定名称的名称空间前缀部分,并且必须与名称空间声明中的名称空间URI引用相关联。

https://www.w3.org/TR/REC-xml-names/#ns-qualnames

所以我认为这是预期的行为:丢弃未声明的名称空间,以便优雅地允许对不遵守建议的文档进行一些解析。

另一答案

改变这一行:

soup = BeautifulSoup(open(filepath_in), 'xml')

soup = BeautifulSoup(open(filepath_in), 'lxml')

要么

soup = BeautifulSoup(open(filepath_in), 'html.parser')

以上是关于使用beautifulsoup4进行xml解析,命名空间问题的主要内容,如果未能解决你的问题,请参考以下文章

爬虫中BeautifulSoup4解析器

Python爬虫开发第1篇beautifulSoup4解析器

Python爬虫(十四)_BeautifulSoup4 解析器

python模块--BeautifulSoup4 和 lxml

Python 之BeautifulSoup4解析模块

爬虫——BeautifulSoup4解析器