In this Article
数据有多种格式,从简单的CSV表格和JSON对象,到描述性更强、层级更清晰的XML文档。XML看起来或许有些老旧,但在RSS源、API和软件配置中依然随处可见。
使用XML数据前,需要先对其进行解析。换句话说,就是让程序读懂其中的内容。借助Python的ElementTree、minidom和lxml等库,XML解析过程清晰直观。
无论面对何种数据格式,学会处理它们,尤其是XML,都是开发人员的必备技能。 在本指南中,您将逐步学习如何从XML中读取、导航和提取数据。
XML 基础
XML是可扩展标记语言,是一种用于存储和传输数据的通用格式。它的结构既便于人类阅读,也便于机器处理。XML的设计注重简洁、适应性和广泛的在线可用性。
XML可以理解为一棵树:它有一个根元素,以及层层嵌套的子元素。这种结构体现了XML以层级方式组织数据的特点。Python提供了多种处理XML数据的方法,其中ElementTree模块最为常用。ElementTree模块包含两个主要类:ElementTree(表示整个文档)和Element(表示单个节点)。
开发者如何解析XML:主要方法
在Python中,parse()函数通常是处理XML文档的第一步。它会解析XML文档,并将其转换为程序可遍历的树结构。无论使用ElementTree、lxml还是其他库,这个函数都是多种解析方法的基础。
要掌握解析方法,首先需要了解XML文档的组成部分:
- 命名空间:通过限定元素和属性的名称来避免命名冲突。
- 根元素 :包含所有其他元素的主容器。
- 属性 :用于提供元素额外信息的键值对。
- 子元素:按层级组织数据的嵌套元素。
- 文字内容:元素中包含的实际信息。
这些部分共同构成结构化格式,使程序能够存储和共享复杂数据。了解结构后,我们来看看Python中主要的XML解析方法:
1. ElementTree模块
Python内置库,提供易用的API来遍历XML树。它兼顾易用性和性能,适合大多数任务。
2. lxml
第三方库,支持XPath、XSLT和验证。它比ElementTree更快、功能更丰富,适合处理复杂的XML。
3. DOM (文档对象模型)
它会将完整的XML文档以树结构加载到内存中。虽然易于遍历和修改,但处理较大文件时可能占用大量内存。
4. SAX (XML简单API)
一种事件驱动的解析器,按顺序处理XML。它内存效率很高,能快速处理大文件,但处理复杂操作时不如其他方法直观。
5. Untangle
将XML数据转换为Python对象,便于访问。它适合用较少代码快速解析,但对复杂XML结构的适应性较弱。
学习本教程,您需要…
- Python和Requests
如果计算机尚未安装Python 3.x,可从以下网址下载:https://www.python.org/。打开终端后,使用pip安装Requests:
pip install requests 或 pip3 install requests
- Visual Studio Code(VS Code)
可使用Visual Studio Code(VS Code)内置终端运行所需命令。只需从 官方网站下载,并按照对应操作系统的安装说明进行操作。
- RSS源
RSS源是一种特殊的XML文档格式,可自动传递网站新闻或更新。简而言之,它是一份以程序可读取方式组织的”最新文章列表”。
例如,我们的网站设有教程专区。与其每天检查网站是否发布了新教程,不如使用RSS源,它是一份包含所有近期文章信息的XML文档。我们将使用 DataImpulse RSS源 进行演示。现在来看看需要的库和代码。
ElementTree
下面介绍如何使用Python内置的ElementTree模块解析RSS源中的XML数据。
首先导入xml.etree.ElementTree。过程很简单:向RSS源URL发送GET请求后,服务器会返回XML数据。响应对象的.content属性会提供可供ElementTree解析的原始字节数据。使用ET.fromstring(),可将XML数据转换为树结构并获得根元素。其中大部分内容位于<channel> → <item> 元素中。
find() 方法返回第一个匹配元素,而findall() 返回具有给定标签的所有子元素。这里的items是元素列表,每个元素代表一篇教程或文章。
代码示例:
import xml.etree.ElementTree as ET
import requests
# URL of the RSS feed (from DataImpulse site)
FEED_URL = "https://dataimpulse.com/tutorials-category/web-scraping-seo/feed/"
# Download the XML data
response = requests.get(FEED_URL)
xml_data = response.content
# Parse the XML
root = ET.fromstring(xml_data)
# In RSS feeds, items are inside -
channel = root.find("channel")
items = channel.findall("item")
# Print titles and links of the items
for item in items:
title = item.findtext("title")
link = item.findtext("link")
print(f"Title: {title}")
print(f"Link: {link}\n")
最后,使用 findtext()直接获取文本内容,并提取和打印每个项目的标题与链接。运行脚本后,将得到一份格式整齐的教程标题及其URL列表。
lxml
lxml是一个第三方库,扩展了ElementTree的功能,并支持XPath这种用于遍历XML文档的查询语言。本例将使用lxml.etree解析RSS源并提取文章标题。
和前面一样,首先使用requests.get()获取RSS源,再将响应内容传给etree.fromstring(),它会解析XML并返回根元素。借助lxml,可以使用XPath表达式查找元素,比标准的ElementTree方法更灵活高效。
import requests
from lxml import etree
# URL of the RSS feed
FEED_URL = "https://dataimpulse.com/tutorials-category/web-scraping-seo/feed/"
# Download the XML data
response = requests.get(FEED_URL)
xml_data = response.content
# Parse XML
root = etree.fromstring(xml_data)
items = root.xpath("//channel/item")
# Show list of titles with numbers
print("Available blog posts:\n")
for item in items:
title = item.findtext("title")
print(title)
最后,遍历 <item> 元素,使用findtext(“title”)提取每篇文章的标题并打印出来。
Minidom
本例使用Minidom,即”最小DOM实现”。它是Python标准库的一部分,会将完整XML文档作为文档对象模型(DOM)加载到内存中。
调用minidom.parseString()来解析XML并构建DOM对象(文档)。这里将使用两个常见的DOM方法。第一个是getElementsByTagName(“tag”,),它会查找具有给定标签名的所有元素。第二个是.firstChild.nodeValue,用于获取节点的文本内容。
最后,使用toprettyxml(). 它会以格式化的XML形式打印第一个item元素。
以下展示如何使用Minidom解析RSS源中的数据:
import requests
from xml.dom import minidom
# URL of the RSS feed
FEED_URL = "https://dataimpulse.com/tutorials-category/web-scraping-seo/feed/"
# Download XML data
response = requests.get(FEED_URL)
xml_data = response.content
# Parse XML with minidom
doc = minidom.parseString(xml_data)
# Get all - elements
items = doc.getElementsByTagName("item")
print(f"Total articles found in feed: {len(items)}\n")
# Show titles with links
for i, item in enumerate(items, start=1):
title = item.getElementsByTagName("title")[0].firstChild.nodeValue
link = item.getElementsByTagName("link")[0].firstChild.nodeValue
print(f"{i}. {title}")
print(f" Link: {link}\n")
# Show raw XML of the first
- (just for demo)
print("First
- as raw XML:\n")
print(items[0].toprettyxml())
SAX
SAX会按顺序逐个读取XML元素,并在处理XML的不同部分时触发特定事件:
- startElement(name, attrs):标签开始时触发
- characters(content):读取标签之间的文本时触发
- endElement(name):标签结束时触发
将代码保存到文件后运行:
import requests
import xml.sax
# Custom handler for SAX parsing
class RSSHandler(xml.sax.ContentHandler):
def __init__(self):
super().__init__()
self.current_tag = ""
self.in_item = False
self.title = ""
self.link = ""
self.counter = 0
def startElement(self, name, attrs):
if name == "item":
self.in_item = True
self.title = ""
self.link = ""
self.current_tag = name
def characters(self, content):
if self.in_item:
if self.current_tag == "title":
self.title += content
elif self.current_tag == "link":
self.link += content
def endElement(self, name):
if name == "item":
self.counter += 1
print(f"{self.counter}. {self.title.strip()}")
print(f" Link: {self.link.strip()}\n")
self.in_item = False
self.current_tag = ""
# Download the XML feed
FEED_URL = "https://dataimpulse.com/tutorials-category/web-scraping-seo/feed/"
response = requests.get(FEED_URL)
xml_data = response.content
# Parse XML with SAX
print("Parsing RSS feed with SAX...\n")
handler = RSSHandler()
xml.sax.parseString(xml_data, handler)
print(f"Total items parsed: {handler.counter}")
这种方法的优点是内存效率高,因为不会存储整个文档。
Untangle
Untangle是处理XML的一种更简单的方法。该库会将XML文档转换为Python对象,因此无需手动遍历元素或处理事件。
使用untangle.parse()时,会自动读取XML文档,将每个XML标签转换为Python对象,并将标签数据存储在.cdata属性中(如果标签包含文本内容)。无需使用节点或事件,只需像使用常规Python对象一样遍历XML结构即可。
代码如下:
import requests
import untangle
from io import BytesIO
# RSS feed from DataImpulse
FEED_URL = "https://dataimpulse.com/tutorials-category/web-scraping-seo/feed/"
# Download XML
resp = requests.get(FEED_URL)
resp.raise_for_status()
xml_bytes = resp.content
# Parse XML with untangle (use BytesIO for bytes input)
doc = untangle.parse(BytesIO(xml_bytes))
# Get all - elements
items = doc.rss.channel.item
print("Articles from DataImpulse RSS:\n")
# Print articles
for i, it in enumerate(items, start=1):
title = it.title.cdata if hasattr(it, "title") else ""
link = it.link.cdata if hasattr(it, "link") else ""
desc = it.description.cdata if hasattr(it, "description") else ""
print(f"{i}. {title}")
print(f" Link: {link}")
if desc:
print(f" Description: {desc}")
print()
其他建议
- 检查解析错误
有时RSS源可能不可用,或包含意外标签。遇到这种情况时,请在下载或解析XML的代码中使用try/except。
- 使用.content下载XML
.content 属性返回原始字节数据,可避免.text 属性 可能导致的编码问题。
- 验证元素是否存在
并非所有XML文档的结构都相同。访问标签前,可使用如下条件判断:if item.find(‘title’) is not None。
- 关注大文件的资源占用
对于大型XML文档,请使用SAX。它会按顺序读取数据,而不会将所有内容加载到内存中。
从外部网站获取XML有时并不容易。代理有助于在不同地区顺畅访问,并保护隐私。
要点回顾
- 解析XML是读取XML文档并以结构化、可用的方式提取数据的过程。
- ElementTree提供基于树的方式来遍历和提取XML元素。
- lxml支持XPath查询,可更快速地解析XML。
- Minidom会将完整XML加载为DOM树,让你像访问结构化对象一样访问节点和文本。
- SAX非常适合大型XML文档,因为它会逐步处理XML,而不会将所有内容保留在内存中。
- Untangle将XML转换为Python对象,便于访问标签及其内容。
