如何用Python中的BeautifulSoup解析XML

706 阅读3分钟

简介

***可扩展标记语言(XML)***是一种标记语言,因其结构数据的方式而流行。它在数据传输(代表序列化的对象)和配置文件中发现了用途。

尽管JSON越来越受欢迎,你仍然可以在Android开发的清单文件、Java/Maven构建工具和网络上的SOAP API中找到XML。因此,解析XML仍然是开发人员必须要做的一项常见任务。

在Python中,我们可以通过利用两个库来读取和解析XML。 BeautifulSoupLXML.

在本指南中,我们将看看如何从XML文件中提取和解析数据,用 BeautifulSoupLXML,并使用Pandas存储结果。

设置LXML和BeautifulSoup

我们首先需要安装这两个库。我们将在你的工作区创建一个新的文件夹,建立一个虚拟环境,然后安装库。

$ mkdir xml_parsing_tutorial
$ cd xml_parsing_tutorial
$ python3 -m venv env # Create a virtual environment for this project
$ . env/bin/activate # Activate the virtual environment
$ pip install lxml beautifulsoup4 # Install both Python packages

现在我们已经设置好了一切,让我们做一些解析吧

用 lxml 和 BeautifulSoup 解析 XML

解析总是取决于底层文件和它所使用的结构,所以没有一个单一的银弹适用于所有文件。BeautifulSoup会自动解析它们,但底层元素是由任务决定的。

因此,最好用实践的方法来学习解析。把下面的XML保存到你工作目录下的一个文件中--teachers.xml

<?xml version="1.0" encoding="UTF-8"?>
<teachers>
    <teacher>
        <name>Sam Davies</name>
        <age>35</age>
        <subject>Maths</subject>
    </teacher>
    <teacher>
        <name>Cassie Stone</name>
        <age>24</age>
        <subject>Science</subject>
    </teacher>
    <teacher>
        <name>Derek Brandon</name>
        <age>32</age>
        <subject>History</subject>
    </teacher>
</teachers>

<teachers> 标签表示XML文档的根,<teacher> 标签是<teachers></teachers> 的一个子元素或子元素,有关于一个单数人的信息。<name>,<age>,<subject><teacher> 标签的子女,是<teachers> 标签的孙子。

上面的示例文档中的第一行,<?xml version="1.0" encoding="UTF-8"?> ,被称为XML prolog。它总是出现在一个XML文件的开头,尽管在一个XML文件中包括一个XML序言是完全可选的。

上面显示的XML序言指出了所使用的XML的版本和字符编码的类型。在这种情况下,XML文档中的字符是以UTF-8编码的。

现在我们了解了XML文件的结构--我们可以解析它了。在你的工作目录中创建一个名为teachers.py 的新文件,并导入BeautifulSoup库。

from bs4 import BeautifulSoup

**注意:**你可能已经注意到,我们没有导入lxml!随着BeautifulSoup的导入,LXML被自动集成,所以单独导入它是没有必要的,但它并没有作为BeautifulSoup的一部分安装。

现在让我们读取我们创建的XML文件的内容,并将其存储在一个叫做soup 的变量中,这样我们就可以开始解析了。

with open('teachers.xml', 'r') as f:
	file = f.read() 

# 'xml' is the parser used. For html files, which BeautifulSoup is typically used for, it would be 'html.parser'.
soup = BeautifulSoup(file, 'xml')

soup 这个变量现在有我们的XML文件的解析内容。我们可以使用这个变量和与之相连的方法,用 Python 代码检索 XML 信息。

比方说,我们只想从 XML 文档中查看教师的名字。我们可以用几行代码得到这些信息。

names = soup.find_all('name')
for name in names:
    print(name.text)

运行python teachers.py ,我们就可以得到。

Sam Davis 
Cassie Stone 
Derek Brandon

find_all() 方法返回一个作为参数传入的所有匹配标签的列表。如上面的代码所示,soup.find_all('name') 返回XML文件中所有的<name> 标签。然后我们遍历这些标签并打印它们的text 属性,其中包含标签的值。

在一个表格中显示解析过的数据

让我们更进一步,我们将解析XML文件的所有内容,并以表格的形式显示。

让我们用重写teachers.py 文件。

from bs4 import BeautifulSoup

# Opens and reads the xml file we saved earlier
with open('teachers.xml', 'r') as f:
    file = f.read()

# Initializing soup variable
soup = BeautifulSoup(file, 'xml')

# Storing <name> tags and elements in names variable
names = soup.find_all('name')

# Storing <age> tags and elements in 'ages' variable
ages = soup.find_all('age')

# Storing <subject> tags and elements in 'subjects' variable
subjects = soup.find_all('subject')

# Displaying data in tabular format
print('-'.center(35, '-'))
print('|' + 'Name'.center(15) + '|' + ' Age ' + '|' + 'Subject'.center(11) + '|')
for i in range(0, len(names)):
    print('-'.center(35, '-'))
    print(
        f'|{names[i].text.center(15)}|{ages[i].text.center(5)}|{subjects[i].text.center(11)}|')
print('-'.center(35, '-'))

上述代码的输出看起来是这样的。

-----------------------------------
|      Name     | Age |  Subject  |
-----------------------------------
|   Sam Davies  |  35 |   Maths   |
-----------------------------------
|  Cassie Stone |  24 |  Science  |
-----------------------------------
| Derek Brandon |  32 |  History  |
-----------------------------------

祝贺你!你刚刚用BeautifulSoup和LXML解析了你的第一个XML文件!现在你对这个理论和过程更加熟悉了,让我们试试一个更真实的例子。

我们将数据格式化为一个表,作为将其存储在一个多功能数据结构中的前奏。也就是说--在即将到来的小项目中,我们将把数据存储在一个PandasDataFrame

解析RSS提要并将数据存储到CSV中

在这一节中,我们将解析一个RSS提要的 纽约时报新闻并将这些数据存储在一个 CSV 文件中。

RSS是以下术语的简称 真正简单的联合传播.一个RSS提要是一个包含网站更新摘要的文件,用XML写成。在这种情况下,《纽约时报》的RSS提要包含其网站上每日新闻更新的摘要。这个摘要包含新闻发布的链接、文章图片的链接、新闻项目的描述等等。RSS源也被用来让人们在不搜刮网站的情况下获得数据,作为网站所有者的一个很好的信物。

这里是纽约时报的一个RSS提要的快照。

Image showcasing an XML document containing news updates relating to the U.S on The New York Times News website.

你可以通过这个链接获得不同大洲、国家、地区、主题和其他标准的纽约时报RSS提要。

在开始解析数据之前,看到并理解数据的结构是很重要的。我们想从RSS提要中提取的关于每篇新闻文章的数据是。

  • 全球唯一标识符(GUID)
  • 标题
  • 出版日期
  • 描述

现在我们已经熟悉了结构并有了明确的目标,让我们开始我们的程序吧我们将需要requests 库和pandas 库来检索数据并轻松地将其转换为CSV文件。

通过requests ,我们可以向网站发出HTTP请求并解析响应。在这种情况下,我们可以用它来检索他们的RSS提要(XML格式),这样BeautifulSoup就可以解析它了。有了pandas ,我们就可以将解析后的数据格式化为一个表,最后将表的内容存储到一个CSV文件中。

在同一个工作目录下,安装requestspandas (你的虚拟环境应该仍然是活动的)。

$ pip install requests pandas

在一个新文件中,nyt_rss_feed.py ,让我们导入我们的库。

import requests
from bs4 import BeautifulSoup
import pandas as pd

然后,让我们向《纽约时报》的服务器发出一个HTTP请求,以获得他们的RSS提要并检索其内容。

url = 'https://rss.nytimes.com/services/xml/rss/nyt/US.xml'
xml_data = requests.get(url).content 

通过上面的代码,我们已经能够从HTTP请求中获得一个响应,并将其内容存储在xml_data 变量中。requests 库返回的数据为bytes

现在,在BeautifulSoup的帮助下,创建以下函数,将XML数据解析成Pandas中的一个表格。

def parse_xml(xml_data):
  # Initializing soup variable
    soup = BeautifulSoup(xml_data, 'xml')

  # Creating column for table
    df = pd.DataFrame(columns=['guid', 'title', 'pubDate', 'description'])

  # Iterating through item tag and extracting elements
    all_items = soup.find_all('item')
    items_length = len(all_items)
    
    for index, item in enumerate(all_items):
        guid = item.find('guid').text
        title = item.find('title').text
        pub_date = item.find('pubDate').text
        description = item.find('description').text

       # Adding extracted elements to rows in table
        row = {
            'guid': guid,
            'title': title,
            'pubDate': pub_date,
            'description': description
        }

        df = df.append(row, ignore_index=True)
        print(f'Appending row %s of %s' % (index+1, items_length))

    return df

上面的函数用BeautifulSoup解析来自HTTP请求的XML数据,将其内容存储在一个soup 。通过df 变量引用带有行和列的Pandas DataFrame,用于我们想要解析的数据。

然后我们在XML文件中进行迭代,找到所有带有<item> 的标签。通过遍历<item> 标签,我们能够提取其子标签。<guid>,<title>,<pubDate>, 和<description> 。注意我们是如何使用find() 方法来获取一个对象的。我们将每个子标签的值追加到Pandas表中。

现在,在文件末尾的函数之后,添加这两行代码来调用函数并创建一个CSV文件。

df = parse_xml(xml_data)
df.to_csv('news.csv')

运行python nyt_rss_feed.py ,在你现在的工作目录下创建一个新的CSV文件。

Appending row 1 of 24
Appending row 2 of 24
...
Appending row 24 of 24

该CSV文件的内容将看起来像这样。

Image showing CSV output of parsed RSS feed for The New York Times

注意:下载数据可能需要一些时间,这取决于你的互联网连接和RSS提要。解析数据也可能需要一些时间,这取决于你的CPU和内存资源。我们使用的源是相当小的,所以它应该很快处理。如果你没有立即看到结果,请耐心等待。

恭喜你,你已经成功地解析了纽约时报新闻的RSS提要,并将其转换为CSV文件!

总结

在本指南中,我们学习了如何设置BeautifulSoup和LXML来解析XML文件。我们首先通过解析一个带有教师数据的简单的XML文件进行了练习,然后我们解析了纽约时报的RSS提要,将他们的数据转换为CSV文件。

你可以使用这些技术来解析你可能遇到的其他XML,并将它们转换成你需要的不同格式