【问题标题】:How to parse XML with namespaces in tags using BeautifulSoup?如何使用 BeautifulSoup 解析带有名称空间的 XML?
【发布时间】:2022-01-01 10:13:48
【问题描述】:

我有一个包含以下数据的 xml 链接 (http://api.worldbank.org/v2/countries):

<wb:countries xmlns:wb="http://www.worldbank.org" page="1" pages="6" per_page="50" total="299">
<wb:country id="ABW">
<wb:iso2Code>AW</wb:iso2Code>
<wb:name>Aruba</wb:name>
<wb:region id="LCN" iso2code="ZJ">Latin America & Caribbean </wb:region>
<wb:adminregion id="" iso2code=""/>
<wb:incomeLevel id="HIC" iso2code="XD">High income</wb:incomeLevel>
<wb:lendingType id="LNX" iso2code="XX">Not classified</wb:lendingType>
<wb:capitalCity>Oranjestad</wb:capitalCity>
<wb:longitude>-70.0167</wb:longitude>
<wb:latitude>12.5167</wb:latitude>
</wb:country>
<wb:country id="AFE">
<wb:iso2Code>ZH</wb:iso2Code>
<wb:name>Africa Eastern and Southern</wb:name>
<wb:region id="NA" iso2code="NA">Aggregates</wb:region>
<wb:adminregion id="" iso2code=""/>
<wb:incomeLevel id="NA" iso2code="NA">Aggregates</wb:incomeLevel>
<wb:lendingType id="" iso2code="">Aggregates</wb:lendingType>
<wb:capitalCity/>
<wb:longitude/>
<wb:latitude/>
</wb:country>
</wb:countries>

我试图解析收入水平,但它返回(无) 如何使用 BeautifulSoup 到达 xml 文本中的文本(例如:高收入)? 我试过这段代码,但它不能正常工作!

import requests
#import re
from bs4 import BeautifulSoup
url = 'http://api.worldbank.org/v2/countries'

response = requests.get(url)
soup = BeautifulSoup(response.content, 'lxml')
countries= soup.findAll('wb:country')
for country in countries:
    name = country.find("wb:name").text
    code = country.find('wb:iso2code').text
    incomeLevel = country.find('wb:incomeLevel', {"iso2code":"XD"})
    print(f"{name}, {code}, {incomeLevel}")

【问题讨论】:

    标签: python xml web-scraping beautifulsoup


    【解决方案1】:

    感谢您发布问题。我认为您的代码中有一些错误。这将帮助您纠正错误。

    1. 您应该使用新方法,即来自 bs4 API 的 find_all,而不是 findAll。请参考此链接https://www.crummy.com/software/BeautifulSoup/bs4/doc/#method-names
    2. BeautifulSoup 的第二个参数中,请指定“lxml-xml”或简单的“xml”,因为它指示beautifulsoup 生成一个XML 文档,否则它只会生成一个纯HTML 文档,并且在您希望解析的问题中并从 XML 文档中提取内容。请参考以下链接https://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser

    请参考以下代码sn-p :)

    import requests
    from bs4 import BeautifulSoup
    
    url = 'http://api.worldbank.org/v2/countries'
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'xml')
    countries = soup.find_all('wb:country')
    for country in countries:
        name = country.find('wb:name').text
        code = country.find('wb:iso2Code').text
        income_level = country.find('wb:incomeLevel').text
        print(f'Name: {name} Code: {code} Income Level: {income_level}')
    

    【讨论】:

    • 无需使用“utf-8-sig”解码。为什么说这是最重要的一点?
    • 尝试按原样使用响应内容。我可以保证它不会被解析。我试过了:)
    • 我也试过了,不需要.decode('utf-8-sig')
    • '
    • 奇怪的字符是字节顺序标记。它可以存在于 UTF-8 文件中,但这不是您应该担心的事情。字节顺序在 UTF-8 中没有意义。为什么会有惊喜呢?
    【解决方案2】:

    以下是如何使用 xml 模块中的 ElementTree 类,尤其要考虑命名空间:

    from xml.etree import ElementTree as ET
    
    ns = {'wb': 'http://www.worldbank.org'}
    
    countries = ET.parse('input.xml').getroot()
    
    for country in countries.findall('wb:country', namespaces=ns):
        name = country.find("wb:name", namespaces=ns).text
        code = country.find('wb:iso2Code', namespaces=ns).text
        
        incomeLevel = None
        for x in country.findall('wb:incomeLevel', namespaces=ns):
            if x.get('iso2code') == 'XD':
                incomeLevel = x.text
                break
        
        print(f"{name}, {code}, {incomeLevel}")
    

    当我在您提供的示例 input.xml 上运行它时,我得到:

    Aruba, AW, High income
    Africa Eastern and Southern, ZH, None
    

    【讨论】:

      【解决方案3】:

      会发生什么?

      解析器在解析带有 xml 命名空间的 BeautifulSoup 对象时遇到问题,因为它是作为 HTML 创建的,这就是为什么你会得到一个 None 并且在你不添加 .text 方法时你不会得到文本.所以它是两者的结合。

      如何实现?

      xml 作为解析器传递给 BeautifulSoup 以正确处理命名空间,并将其作为有效的 xml 而不是 html:

      soup = BeautifulSoup(response.content, 'xml')
      

      .text 添加到您的结果中:

      incomeLevel = country.find('incomeLevel').text
      

      如果您只想获取具有incomeLevel 和iso2code="XD" 的国家/地区,请更改您的选择器并使用css selectors 而不是find_all()

      countries = soup.select('country:has(incomeLevel[iso2code="XD"])')
      for country in countries:
          name = country.find("name").text
          code = country.find('iso2Code').text
          incomeLevel = country.find('incomeLevel').text
          print(f"{name}, {code}, {incomeLevel}")
      

      注意: xml 解析器区分大小写 find('iso2code') 不起作用,您必须更改为 find('iso2Code')

      示例

      注意: 在新代码中最好使用实际语法find_all() 而不是过时的findAll()

      import requests
      #import re
      from bs4 import BeautifulSoup
      url = 'http://api.worldbank.org/v2/countries'
      
      response = requests.get(url)
      soup = BeautifulSoup(response.content, 'xml')
      countries= soup.find_all('country')
      for country in countries:
          name = country.find("name").text
          code = country.find('iso2Code').text
          incomeLevel = country.find('incomeLevel').text
          print(f"{name}, {code}, {incomeLevel}")
      

      输出

      Aruba, AW, High income
      Africa Eastern and Southern, ZH, Aggregates
      Afghanistan, AF, Low income
      Africa, A9, Aggregates
      Africa Western and Central, ZI, Aggregates
      Angola, AO, Lower middle income
      Albania, AL, Upper middle income
      Andorra, AD, High income
      Arab World, 1A, Aggregates
      United Arab Emirates, AE, High income
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-17
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-01
        相关资源
        最近更新 更多