繁体   English   中英

如何使用 BeautifulSoup 访问命名空间 XML 元素?

[英]How can I access namespaced XML elements using BeautifulSoup?

我有一个 XML 文档,内容如下:

<xml>
<web:Web>
<web:Total>4000</web:Total>
<web:Offset>0</web:Offset>
</web:Web>
</xml>

我的问题是如何使用 python 中的 BeautifulSoup 之类的库访问它们?

xmlDom.web["Web"]. 总计? 不起作用?

BeautifulSoup本身并不是一个 DOM 库(它没有实现 DOM API)。 更复杂的是,您在该 xml 片段中使用了名称空间。 要解析特定的 XML 片段,您可以按如下方式使用 BeautifulSoup:

from BeautifulSoup import BeautifulSoup

xml = """<xml>
  <web:Web>
    <web:Total>4000</web:Total>
    <web:Offset>0</web:Offset>
  </web:Web>
</xml>"""

doc = BeautifulSoup( xml )
print doc.find( 'web:total' ).string
print doc.find( 'web:offset' ).string

如果您没有使用命名空间,则代码可能如下所示:

from BeautifulSoup import BeautifulSoup

xml = """<xml>
  <Web>
    <Total>4000</Total>
    <Offset>0</Offset>
  </Web>
</xml>"""

doc = BeautifulSoup( xml )
print doc.xml.web.total.string
print doc.xml.web.offset.string

这里的关键是 BeautifulSoup 对命名空间一无所知(或关心)。 因此web:Web被视为web:web标签,而不是属于web命名空间的Web标签。 虽然 BeautifulSoup 将web:web添加到 xml 元素字典中,但 python 语法不会将web:web识别为单个标识符。

您可以通过阅读文档了解更多信息。

这是一个老问题,但有人可能不知道,如果您将'xml'作为第二个参数传递给构造函数,那么至少BeautifulSoup 4可以很好地处理命名空间:

soup = BeautifulSoup("""<xml>
<web:Web>
<web:Total>4000</web:Total>
<web:Offset>0</web:Offset>
</web:Web>
</xml>""", 'xml')

print soup.prettify()
<?xml version="1.0" encoding="utf-8"?>
<xml>
 <Web>
  <Total>
   4000
  </Total>
  <Offset>
   0
  </Offset>
 </Web>
</xml>

您应该使用xmlns:prefix="URI"语法(请参阅此处的示例)在根元素上明确定义您的命名空间,然后通过 BeautifulSoup 中的prefix:tag访问您的属性。 请记住,在这种情况下,您还应该明确定义 BeautifulSoup 应该如何处理您的文档:

xml = BeautifulSoup(xml_content, 'xml')

环境

import bs4
bs4.__version__
---
4.10.0'

import sys
print(sys.version)
---
3.8.10 (default, Nov 26 2021, 20:14:08) 
[GCC 9.3.0]

带有命名空间定义的 XML 上的 BS4/XML 解析器

from bs4 import BeautifulSoup

xbrl_with_namespace = """
<?xml version="1.0" encoding="UTF-8"?>
<xbrl
    xmlns:dei="http://xbrl.sec.gov/dei/2020-01-31"
>
<dei:EntityRegistrantName>
Hoge, Inc.
</dei:EntityRegistrantName>
</xbrl>
"""

soup = BeautifulSoup(xbrl_with_namespace, 'xml')
registrant = soup.find("dei:EntityRegistrantName")
print(registrant.prettify())
---
<dei:EntityRegistrantName>
Hoge, Inc.
</dei:EntityRegistrantName>

没有命名空间定义的 XML 上的 BS4/XML 解析器

xbrl_without_namespace = """
<?xml version="1.0" encoding="UTF-8"?>
<dei:EntityRegistrantName>
Hoge, Inc.
</dei:EntityRegistrantName>
</xbrl>
"""

soup = BeautifulSoup(xbrl_without_namespace, 'xml')
registrant = soup.find("dei:EntityRegistrantName")
print(registrant)
---
None

没有命名空间定义的 XML 上的 BS4/HTML 解析器

BS4/HTML 解析器将<namespace>:<tag>视为单个标签,此外还降低了字母。

soup = BeautifulSoup(xbrl_without_namespace, 'html.parser')
registrant = soup.find("dei:EntityRegistrantName".lower()) 

print(registrant)
---
<dei:entityregistrantname>
Hoge, Inc.
</dei:entityregistrantname>

与大写字母不匹配,因为它们已转换为小写字母。

registrant = soup.find("dei:EntityRegistrantName") 
print(registrant)
---
None

结论

  1. 提供命名空间定义以将命名空间与 XML 解析器一起使用,或者
  2. 使用 HTML 解析器并处理所有小写字母。

对于下面的示例,我假设您:

  1. 在 XML 文件的顶部声明名称空间: xmlns:ns_name="http://example.com"
  2. 将您的 XML 解析为 xml: BeautifulSoup(data, 'xml')

提取命名空间中的已知标签

如果<ns_name:tag_name>已知,则find()find_all()方法将正常工作 - 如本线程中所述。

# extract the first element with tag name
xml_soup.find('web:Web')

# extract all elements with tag name
xml_soup.find_all('web:Web')

使用 CSS 选择器在命名空间中搜索

BS4 还允许您使用前缀CSS 选择器在命名空间内进行搜索:您的命名空间,一个 pipe 符号| 最后是您的 CSS 选择器。 模板: ns_name|css_selector

# select all elements in the namespace 'web'
xml_soup.select('web|*')

# selecting specific elements within the namespace 'web'
xml_soup.select('web|Web > Total')

名称空间内更复杂的搜索

对于任何更复杂的事情,您需要编写自定义 boolean function:

def ns_and_regex_match(tag) -> bool:
  if tag.prefix != 'web':
    return False
  return bool(re.search('^Off.*$', tag.name))

xml_soup.find_all(ns_and_regex_match)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM