当前位置: 首页 > news >正文

五分钟XML速成

原文链接:

XML - Dive Into Python 3

深入探讨

本书几乎所有章节都围绕一段示例代码展开,但 XML 并非关于代码,而是关于数据

XML 的一个常见用途是 “聚合提要”(syndication feeds),用于列出博客、论坛或其他频繁更新网站上的最新文章。大多数主流博客软件都能生成提要,并在发布新文章、讨论帖或博文时对其进行更新。你可以通过 “订阅” 博客的提要来关注该博客,也可以使用专门的 “提要聚合器”(如谷歌阅读器)同时关注多个博客。

接下来,本章将围绕以下 XML 数据展开讲解,它是一个提要,具体来说,是一个 Atom 聚合提要。

<?xml version='1.0' encoding='utf-8'?> 
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'><title>dive into mark</title><subtitle>currently between addictions</subtitle><id>tag:diveintomark.org,2001-07-29:/</id><updated>2009-03-27T21:56:07Z</updated><link rel='alternate' type='text/html' href='http://diveintomark.org/'/><link rel='self' type='application/atom+xml' href='http://diveintomark.org/feed/'/><entry><author><name>Mark</name><uri>http://diveintomark.org/</uri></author><title>Dive into history, 2009 edition</title><link rel='alternate' type='text/html'href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/><id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id><updated>2009-03-27T21:56:07Z</updated><published>2009-03-27T17:20:42Z</published><category scheme='http://diveintomark.org' term='diveintopython'/><category scheme='http://diveintomark.org' term='docbook'/><category scheme='http://diveintomark.org' term='html'/><summary type='html'> Putting an entire chapter on one page soundsbloated, but consider this &amp;mdash; my longest chapter so farwould be 75 printed pages, and it loads in under 5 seconds&amp;hellip;On dialup. </summary></entry><entry><author><name>Mark</name><uri>http://diveintomark.org/</uri></author><title>Accessibility is a harsh mistress</title><link rel='alternate' type='text/html'href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/><id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id><updated>2009-03-22T01:05:37Z</updated><published>2009-03-21T20:09:28Z</published><category scheme='http://diveintomark.org' term='accessibility'/><summary type='html'> The accessibility orthodoxy does not permit people toquestion the value of features that are rarely useful and rarely used. </summary></entry><entry><author><name>Mark</name></author><title>A gentle introduction to video encoding, part 1: container formats</title><link rel='alternate' type='text/html'href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/><id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id><updated>2009-01-11T19:39:22Z</updated><published>2008-12-18T15:54:22Z</published><category scheme='http://diveintomark.org' term='asf'/><category scheme='http://diveintomark.org' term='avi'/><category scheme='http://diveintomark.org' term='encoding'/><category scheme='http://diveintomark.org' term='flv'/><category scheme='http://diveintomark.org' term='GIVE'/><category scheme='http://diveintomark.org' term='mp4'/><category scheme='http://diveintomark.org' term='ogg'/><category scheme='http://diveintomark.org' term='video'/><summary type='html'> These notes will eventually become part of atech talk on video encoding. </summary></entry> 
</feed>

五分钟 XML 速成课程

XML 是一种用于描述层级结构化数据的通用方式。

一个 XML 文档包含一个或多个元素,元素由开始标签和结束标签界定。以下是一个完整(尽管内容单调)的 XML 文档:

<foo>  ① </foo> ②
标记说明
这是foo元素的开始标签。
这是foo元素对应的结束标签。就像写作、数学运算或代码中的括号需要配对一样,每个开始标签都必须有对应的结束标签与之匹配(闭合)。

元素可以进行任意深度的嵌套。在foo元素内部的bar元素,被称为foo元素的子元素。

根元素

<foo><bar></bar> 
</foo>

每个 XML 文档中的第一个元素称为根元素,一个 XML 文档只能有一个根元素

以下内容不能称为 XML 文档,因为它包含两个根元素:

<foo></foo> 
<bar></bar>

属性

元素可以拥有属性,属性是以 “名称 - 值” 对形式存在的数据。

属性列在元素的开始标签内,通过空格分隔。同一个元素内不能重复出现相同名称的属性,且属性值必须用引号括起来,单引号和双引号均可使用

<foo lang='en'> ①   <bar id='papayawhip' lang="fr"></bar> ② 
</foo>
标记说明
foo元素有一个属性,名称为lang,其属性值为en
bar元素有两个属性,名称分别为idlang,其中lang属性的值为fr。这与foo元素的lang属性并不冲突,因为每个元素都有自己独立的属性集合。

若一个元素拥有多个属性,属性的顺序并不重要

元素的属性就像 Python 中的字典,是一组无序的 “键 - 值” 对,且每个元素可定义的属性数量没有限制。

文本内容

元素还可以包含文本内容,示例如下:

<foo lang='en'><bar lang='fr'>PapayaWhip</bar> 
</foo>

既不包含文本内容也没有子元素的元素称为空元素,示例如下:

空元素

<foo></foo>

空元素有一种简写形式,在开始标签中加入/字符,就可以省略结束标签。因此,上面示例中的 XML 文档也可写成:

<foo/>

命名空间

就像 Python 函数可以在不同模块中定义一样,XML 元素也可以在不同的命名空间中定义。

命名空间通常以 URL 的形式呈现

可以使用xmlns声明来定义默认命名空间,这种声明形式类似属性,但用途不同。

<feed xmlns='http://www.w3.org/2005/Atom'> ①   <title>dive into mark</title>              ② 
</feed>
标记说明
feed元素处于http://www.w3.org/2005/Atom命名空间中。
title元素同样处于http://www.w3.org/2005/Atom命名空间中。命名空间声明不仅对其所在的元素生效,还对该元素的所有子元素生效。

也可以使用xmlns:prefix声明来定义命名空间,并将其与一个前缀相关联。

之后,该命名空间下的每个元素都必须显式地使用这个前缀来声明

<atom:feed xmlns:atom='http://www.w3.org/2005/Atom'> ①   <atom:title>dive into mark</atom:title>              ② 
</atom:feed>
标记说明
feed元素处于http://www.w3.org/2005/Atom命名空间中。
title元素同样处于http://www.w3.org/2005/Atom命名空间中。

对于 XML 解析器而言,上述两个 XML 文档是完全相同的。

在 XML 中,元素的标识由 “命名空间 + 元素名称” 共同构成

前缀的作用仅仅是引用命名空间,因此前缀的实际名称(如这里的atom:)并不重要。只要两个 XML 文档的命名空间匹配、元素名称匹配、属性(或无属性的情况)匹配,并且每个元素的文本内容也匹配,那么这两个 XML 文档就是相同的。

字符编码信息

最后,XML 文档可以在首行(根元素之前)包含字符编码信息。(如果您好奇,文档解析前需要知晓编码信息,而编码信息又包含在文档内,这看似矛盾,XML 规范的 F 部分详细说明了如何解决这一 “Catch-22” 困境 —— 即左右为难的局面。)

<?xml version='1.0' encoding='utf-8'?>

至此,您对 XML 的了解已足够应对基础应用场景!

XML 命名空间(namespace)

所谓的namespace,就是一种已经定义好的规范,我只要加了这个namespace对应的链接,我的xml文档的格式就要遵循这个规范,有什么子标签,有什么属性,以及他们的顺序,就不能自己发挥了。

1. 命名空间 = “规范的 “身份标识”,关联它就意味着 “遵循对应的格式规则”

命名空间的核心价值 ——通过一个唯一的 “链接(URI)”,把你的 XML 文档和一份预先定义好的 “官方规范” 绑定
这个 “链接” 本身不是用来 “访问” 的,而是一个 “身份 ID”,告诉解析工具(如浏览器、数据处理程序):“我的文档遵循的是【这个 ID 对应的规范】,所有标签和属性都要按它的规则来校验”。

比如之前提到的 Atom 提要:

  • 你在根标签<feed>里加了xmlns="http://www.w3.org/2005/Atom",这个http://www.w3.org/2005/Atom就是 Atom 规范的 “namespace ID”;
  • 一旦加了它,你的 XML 就必须符合 W3C 制定的《Atom Syndication Format》规范:必须有<title>(标题)、<updated>(更新时间)等强制子标签,<author>标签下必须包含<name>(作者名),标签顺序也有明确要求(比如<updated>要在<entry>之前)—— 完全不能自己随便加标签(比如乱加一个<my-own-tag>),也不能漏了强制标签,否则就是 “不符合规范的 Atom 文档”,解析工具会报错或无法识别。

2. 命名空间的 “额外功能”—— 解决 “标签重名冲突”

除了 “绑定规范”,命名空间还有一个重要作用:当一份 XML 文档需要同时用多个规范时,区分 “同名但来自不同规范的标签”

比如你要写一个既包含 “Atom 提要”(展示文章)、又包含 “SVG 图标”(展示小图标)的 XML 文档:

  • Atom 规范里有<title>标签(表示文章标题);
  • SVG 规范里也有<title>标签(表示图标的标题);
    如果不加命名空间,解析工具根本分不清<title>是 “文章标题” 还是 “图标标题”。

这时候就可以用命名空间 “给标签贴标签”:

<!-- 声明两个命名空间:atom对应Atom规范,svg对应SVG规范 -->
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom" xmlns:svg="http://www.w3.org/2000/svg"><!-- 用“atom:”前缀表示这个<title>属于Atom规范(文章标题) --><atom:title>我的博客</atom:title><!-- 用“svg:”前缀表示这个<title>属于SVG规范(图标标题) --><svg:svg width="100" height="100"><svg:title>首页图标</svg:title></svg:svg>
</atom:feed>

这里的命名空间,既各自绑定了 Atom 和 SVG 的规范(标签仍需遵循对应规则),又解决了 “同名标签冲突”—— 这是对 “规范绑定” 功能的补充,但核心依然是 “通过 ID 关联规则”。

3. “遵循规范” 的 “强制性”,依赖 “解析工具的校验”

“不能自己发挥”,本质上是 “如果自己发挥,解析工具会不识别或报错”—— 因为规范本身是 “约定”,而 “强制遵循” 的落地,需要工具(如 XML 校验器、浏览器)去 “对照规范检查你的文档”

如果只是加了 namespace 链接,但没有用工具校验,文档语法上可能没报错,但实际上不符合规范(比如漏了 Atom 的<updated>标签),最终还是无法被 Atom 阅读器正常订阅 —— 所以 “遵循规范” 的关键,是让文档符合 namespace 对应的 “官方规则”,而不只是表面加个链接。

总结:

namespace 是 “规范的唯一 ID”,加了它既意味着你的 XML 要遵循该 ID 对应的官方规则(子标签、属性、顺序都有要求),也能解决多规范共存时的标签重名问题,最终让你的文档能被工具正确解析和使用。

namespace示例:Atom 提要的结构

试想一个博客网站,或者任何一个内容频繁更新的网站(如CNN.com)。

这类网站通常会有标题(例如CNN.com的标题是 “CNN.com”)、副标题(如 “突发新闻、美国新闻、国际新闻、天气预报、娱乐及视频新闻”)、最后更新日期(如 “美国东部时间 2009 年 5 月 16 日星期六下午 12:43 更新”),以及一系列在不同时间发布的文章。

每篇文章也都有标题、首次发布日期(若文章后续有更正或修改错别字,还会有最后更新日期)和唯一的统一资源定位符(URL)。

Atom 聚合格式旨在以一种标准格式整合所有这些信息。

我的博客与CNN.com在设计风格、涵盖范围和目标受众上差异极大,但它们都具备相同的基本结构:CNN.com有标题,我的博客也有标题;CNN.com发布文章,我也发布文章。

Atom 提要的顶层是所有 Atom 提要共有的根元素 —— 处于http://www.w3.org/2005/Atom命名空间下的feed元素。

<feed xmlns='http://www.w3.org/2005/Atom'  ①   xml:lang='en'>                       ②
标记说明
http://www.w3.org/2005/Atom是 Atom 命名空间。
任何元素都可以包含xml:lang属性,该属性用于声明元素及其子元素的语言。在本示例中,根元素上声明了xml:lang属性,这意味着整个提要的语言为英语。

一个 Atom 提要包含多项关于提要本身的信息,这些信息以顶层feed元素的子元素形式存在。

<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>   <title>dive into mark</title>                                      ①   <subtitle>currently between addictions</subtitle>                  ②   <id>tag:diveintomark.org,2001-07-29:/</id>                         ③   <updated>2009-03-27T21:56:07Z</updated>                            ④   <link rel='alternate' type='text/html' href='http://diveintomark.org/'/>  ⑤
标记说明
该提要的标题为dive into mark
该提要的副标题为currently between addictions
每个提要都需要一个全球唯一的标识符,关于如何创建该标识符,可参考 RFC 4151(一种请求评论文档,用于定义互联网标准)。
该提要的最后更新时间为 2009 年 3 月 27 日格林尼治标准时间 21:56,通常这个时间与最新文章的最后修改时间一致。
接下来的内容会更有趣。这个link元素没有文本内容,但有三个属性:reltypehrefrel属性的值表明了链接的类型,rel='alternate'表示该链接指向该提要的另一种呈现形式;type='text/html'属性表明这是一个指向超文本标记语言(HTML)页面的链接;而链接的目标地址则由href属性给出。

至此,我们了解到这个提要对应的网站名为 “dive into mark”,网站地址为http://diveintomark.org/,且该提要的最后更新时间为 2009 年 3 月 27 日。

需要注意的是,在某些 XML 文档中元素的顺序可能很重要,但在 Atom 提要中,元素的顺序无关紧要。

在提要级元数据之后,是最新文章的列表。一篇文章在 Atom 提要中的结构如下所示:

<entry>   <author>                                                          ①<name>Mark</name><uri>http://diveintomark.org/</uri></author>   <title>Dive into history, 2009 edition</title>                    ②   <link rel='alternate' type='text/html'                           ③href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>   <id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>  ④   <updated>2009-03-27T21:56:07Z</updated>                           ⑤<published>2009-03-27T17:20:42Z</published>   <category scheme='http://diveintomark.org' term='diveintopython'/>  ⑥<category scheme='http://diveintomark.org' term='docbook'/><category scheme='http://diveintomark.org' term='html'/>   <summary type='html'>Putting an entire chapter on one page sounds     ⑦bloated, but consider this &amp;mdash; my longest chapter so farwould be 75 printed pages, and it loads in under 5 seconds&amp;hellip;On dialup. </summary> 
</entry>                                                             ⑧
标记说明
author元素表明了该文章的作者信息:作者名为马克(Mark),其个人网站地址为http://diveintomark.org/。(该地址与提要元数据中的备用链接地址相同,但这并非必需的情况。许多博客有多位作者,每位作者都可能拥有自己的个人网站。)
title元素给出了文章的标题 ——“Dive into history, 2009 edition”(《深入探索历史,2009 年版》)。
与提要级别的备用链接类似,这个link元素提供了该文章 HTML 版本的地址。
和提要一样,每篇文章(entry)也需要一个唯一的标识符。
文章包含两个日期信息:首次发布日期(由published元素指定)和最后修改日期(由updated元素指定)。
文章可以被归类到任意数量的类别中。本文被归类到 `

ElementTree:解析 XML(Parsing XML)

Python 提供了多种解析 XML 文档的方式,包括传统的 DOM(文档对象模型)解析器和 SAX(简单 API for XML)解析器,但本章将重点介绍另一个名为ElementTree的库。

parse()函数:

>>> import xml.etree.ElementTree as etree ①>>> tree = etree.parse('examples/feed.xml') ②
>>> root = tree.getroot() ③
>>> root ④
<Element {http://www.w3.org/2005/Atom}feed at cd1eb0>
标记说明
ElementTree 库是 Python 标准库的一部分,位于xml.etree.ElementTree模块中。
ElementTree 库的主要入口是parse()函数,该函数可接收文件名或类文件对象作为参数,一次性解析整个 XML 文档。若内存紧张,也可采用增量方式解析 XML 文档。
parse()函数返回一个代表整个文档的对象,该对象并非根元素。需调用getroot()方法才能获取根元素的引用
正如预期,根元素是处于http://www.w3.org/2005/Atom命名空间下的feed元素。该对象的字符串表示形式印证了一个重要观点:XML 元素由其命名空间标签名(也称为本地名)共同构成。由于此文档中所有元素均处于 Atom 命名空间,因此根元素被表示为{http://www.w3.org/2005/Atom}feed

在 ElementTree 中,XML 元素以{命名空间}本地名的格式表示,你会在 ElementTree API 的多个场景中看到并使用这种格式。

元素即列表(Elements Are Lists)

在 ElementTree API 中,一个元素的行为类似列表,列表中的项即为该元素的子元素。

# 承接上一个示例
>>> root.tag ①
'{http://www.w3.org/2005/Atom}feed'>>> len(root) ②
8>>> for child in root: ③
...     print(child) ④
... 
<Element {http://www.w3.org/2005/Atom}title at e2b5d0>
<Element {http://www.w3.org/2005/Atom}subtitle at e2b4e0>
<Element {http://www.w3.org/2005/Atom}id at e2b6c0>
<Element {http://www.w3.org/2005/Atom}updated at e2b6f0>
<Element {http://www.w3.org/2005/Atom}link at e2b4b0>
<Element {http://www.w3.org/2005/Atom}entry at e2b720>
<Element {http://www.w3.org/2005/Atom}entry at e2b510>
<Element {http://www.w3.org/2005/Atom}entry at e2b750>
标记说明
承接上一个示例,根元素为{http://www.w3.org/2005/Atom}feed
根元素的 “长度” 即其包含的子元素数量。
可将元素本身用作迭代器,遍历其所有子元素。
从输出可见,根元素确实包含 8 个子元素:所有提要级元数据(titlesubtitleidupdatedlink), followed by the three entry elements.

或许你已经猜到,但在此仍需明确说明:元素的子元素列表仅包含直接子元素

每个entry元素都有自己的子元素,但这些子元素不会出现在根元素feed的子元素列表中,它们只会被包含在各自所属的entry元素的子元素列表里。

不过,也有方法可以查找任意嵌套层级的元素,本章后续将介绍两种常用方法。

属性即字典(Attributes Are Dictionaries)

XML 不仅由元素构成,每个元素还可拥有自己的属性集合。只要获取到特定元素的引用,就能轻松将其属性以 Python 字典的形式获取。

# 承接上一个示例
>>> root.attrib ①
{'{http://www.w3.org/XML/1998/namespace}lang': 'en'}>>> root[4] ②
<Element {http://www.w3.org/2005/Atom}link at e181b0>>>> root[4].attrib ③
{'href': 'http://diveintomark.org/','type': 'text/html','rel': 'alternate'}>>> root[3] ④
<Element {http://www.w3.org/2005/Atom}updated at e2b4e0>>>> root[3].attrib ⑤
{}
标记说明
attrib属性是元素属性的字典形式。原始标记为<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>,其中xml:前缀引用的是一个内置命名空间,所有 XML 文档无需声明即可使用该命名空间。
第 5 个子元素(在 0 索引列表中为[4])是link元素。
link元素有 3 个属性:hreftyperel
第 4 个子元素(在 0 索引列表中为[3])是updated元素。
updated元素没有任何属性,因此其.attrib属性是空字典。

在 XML 文档中搜索节点

到目前为止,我们对 XML 文档的操作都是 “自上而下” 的:从根元素开始,获取其子元素,再依次深入文档结构。

1、查找特定元素:findall()方法

但在许多 XML 使用场景中,需要查找特定元素,ElementTree 也支持这一操作。

>>> import xml.etree.ElementTree as etree>>> tree = etree.parse('examples/feed.xml')>>> root = tree.getroot()>>> root.findall('{http://www.w3.org/2005/Atom}entry') ①
[<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,<Element {http://www.w3.org/2005/Atom}entry at e2b510>,<Element {http://www.w3.org/2005/Atom}entry at e2b540>]>>> root.tag
'{http://www.w3.org/2005/Atom}feed'>>> root.findall('{http://www.w3.org/2005/Atom}feed') ②[]
>>> root.findall('{http://www.w3.org/2005/Atom}author') ③
[]
标记说明
findall()方法用于查找符合特定查询条件的子元素(查询格式详情稍后介绍)。
包括根元素在内的所有元素都拥有findall()方法,该方法会在元素的子元素中查找所有匹配项。但为何此处没有返回结果?尽管不那么明显,但该查询仅搜索当前元素的直接子元素。由于根元素feed没有名为feed的子元素,因此查询返回空列表。
这个结果可能也会让你意外:文档中确实存在author元素(每个entry元素中各有一个,共 3 个),但这些author元素并非根元素的直接子元素,而是 “孙元素”(即子元素的子元素)。若要查找任意嵌套层级的author元素,查询格式需稍作调整。
>>> tree.findall('{http://www.w3.org/2005/Atom}entry') ①
[<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>,<Element {http://www.w3.org/2005/Atom}entry at e2b510>,<Element {http://www.w3.org/2005/Atom}entry at e2b540>]>>> tree.findall('{http://www.w3.org/2005/Atom}author') ②
[]
标记说明
为方便使用,tree对象(由etree.parse()函数返回)提供了多个与根元素方法对应的镜像方法,其结果与调用tree.getroot().findall()方法完全一致。
或许令人意外,该查询并未找到文档中的author元素。原因在于,这只是tree.getroot().findall('{http://www.w3.org/2005/Atom}author')的简写形式,含义是 “查找所有作为根元素子元素的author元素”。而author元素是entry元素的子元素,并非根元素的子元素,因此查询无匹配结果。

2、find()方法

此外,还有一个find()方法,该方法返回第一个匹配的元素

在仅预期一个匹配项,或存在多个匹配项但只需关注第一个时,此方法非常实用。

>>> entries = tree.findall('{http://www.w3.org/2005/Atom}entry') ①
>>> len(entries)
3>>> title_element = entries[0].find('{http://www.w3.org/2005/Atom}title') ②>>> title_element.text
'Dive into history, 2009 edition'>>> foo_element = entries[0].find('{http://www.w3.org/2005/Atom}foo') ③>>> foo_element>>> type(foo_element)
<class 'NoneType'>
标记说明
如前所述,该语句查找所有atom:entry元素。
find()方法接收一个 ElementTree 查询条件,返回第一个匹配的元素。
entry元素中不存在名为foo的元素,因此返回None

【注意】:

find()方法存在一个容易让人踩坑的 “陷阱”:

在布尔上下文中,若 ElementTree 元素对象没有子元素(即len(element)为 0),其布尔值会被判定为False。这意味着,if element.find('...')并非判断find()方法是否找到匹配元素,而是判断找到的匹配元素是否有子元素!

若要判断find()方法是否返回了元素,应使用if element.find('...') is not None

3、查找后代元素://

ElementTree 也支持查找后代元素(即子元素、孙元素及任意嵌套层级的元素),具体方式如下:

>>> all_links = tree.findall('//{http://www.w3.org/2005/Atom}link') ①
>>> all_links
[<Element {http://www.w3.org/2005/Atom}link at e181b0>,<Element {http://www.w3.org/2005/Atom}link at e2b570>,<Element {http://www.w3.org/2005/Atom}link at e2b480>,<Element {http://www.w3.org/2005/Atom}link at e2b5a0>]>>> all_links[0].attrib ②
{'href': 'http://diveintomark.org/','type': 'text/html','rel': 'alternate'}>>> all_links[1].attrib ③
{'href': 'http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition','type': 'text/html','rel': 'alternate'}>>> all_links[2].attrib
{'href': 'http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress','type': 'text/html','rel': 'alternate'}>>> all_links[3].attrib
{'href': 'http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats','type': 'text/html','rel': 'alternate'}
标记说明
该查询//{http://www.w3.org/2005/Atom}link与之前的示例类似,唯一区别是查询开头的两个斜杠(//。这两个斜杠表示 “不局限于直接子元素,查找文档中任意嵌套层级的匹配元素”,因此结果返回 4 个link元素,而非仅 1 个。
第一个结果是根元素的直接子元素,从其属性可知,这是提要级别的备用链接,指向该提要所描述网站的 HTML 版本。
另外 3 个结果均为条目级别的备用链接:每个entry元素都有一个link子元素,正是由于查询开头的双斜杠,这些元素才被全部找到。

总结:

总体而言,ElementTree 的findall()方法功能强大,但查询语言可能存在一些反直觉的设计。官方将其描述为 “对 XPath 表达式的有限支持”——XPath 是 W3C 制定的 XML 文档查询标准。ElementTree 的查询语言与 XPath 足够相似,可满足基础搜索需求,但差异也足够大,若你已熟悉 XPath,可能会对此感到不便。接下来,我们将介绍一个第三方 XML 库lxml,它在 ElementTree API 的基础上扩展了完整的 XPath 支持。


文章转载自:

http://icbtMwXQ.tfkqc.cn
http://9x2DfWPQ.tfkqc.cn
http://ZBWU75Bz.tfkqc.cn
http://gLZqnDcL.tfkqc.cn
http://W1iCRQzD.tfkqc.cn
http://jGEbHJ5V.tfkqc.cn
http://J0iKT4mQ.tfkqc.cn
http://6tGosMgM.tfkqc.cn
http://8thBwEPp.tfkqc.cn
http://0Pz2OZi7.tfkqc.cn
http://5dmUNTG1.tfkqc.cn
http://4pP3NiMj.tfkqc.cn
http://qx0wr7kh.tfkqc.cn
http://YlMvQNEX.tfkqc.cn
http://LNNT3L46.tfkqc.cn
http://tHCP6drd.tfkqc.cn
http://6DmAagpM.tfkqc.cn
http://ro3emvYs.tfkqc.cn
http://wRTFvUFN.tfkqc.cn
http://Pl6JDb1L.tfkqc.cn
http://GjvLeIHX.tfkqc.cn
http://tpKFUAEV.tfkqc.cn
http://TIjNYzaH.tfkqc.cn
http://8J6QEbGF.tfkqc.cn
http://ZxcEJs73.tfkqc.cn
http://Ke3pcAI1.tfkqc.cn
http://Ab8mm4wP.tfkqc.cn
http://fbLNW2qY.tfkqc.cn
http://nYu8sSrQ.tfkqc.cn
http://x3cm2L0I.tfkqc.cn
http://www.dtcms.com/a/367846.html

相关文章:

  • 《LangChain从入门到精通》系统学习教材大纲
  • 华为云 OBS:数字时代的数据存储与价值挖掘利器
  • Claude Code PM 深度实战指南:AI驱动的GitHub项目管理与并行协作
  • paimon实战 -- 阿里基于StarRocks 与 Paimon 实现的实时分析黑科技
  • 开源定时器教程:Quartz与XXL-JOB全面对比与实践
  • Day21_【机器学习—决策树(3)—剪枝】
  • finalize() 方法介绍
  • IDE mac M芯片安装报错:如何解决“InsCode.app 已损坏”,无法打开
  • Qt信号与槽机制全面解析
  • Qt实现背景滚动
  • 新后端漏洞(上)- H2 Database Console 未授权访问
  • 使用CI/CD部署后端项目(gin)
  • Charles抓包工具在接口性能优化与压力测试中的实用方法
  • Spring Boot启动失败从循环依赖到懒加载配置的深度排查指南
  • iOS混淆工具实战 在线教育直播类 App 的课程与互动安全防护
  • uni-app 项目 iOS 上架效率优化 从工具选择到流程改进的实战经验
  • solidity的高阶语法
  • 大数据框架对比与选择指南
  • 啥是两化融合?
  • 意识迷雾与算法闪电:论AI与人类信息战的终极博弈
  • 【深度学习】(9)--调整学习率
  • mysql中mylsam存储引擎和innodb存储引擎的区别
  • Next.js App Router 中文件系统路由与页面跳转实践(以用户详情页面为例)
  • 当 AI 走进千行百业:制造业质检与医疗影像诊断的落地差异分析
  • WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析10
  • 驱动开发系列70 - vkQueueSubmit实现
  • 桌面应用开发语言与框架选择指南
  • 《The Landscape of Agentic Reinforcement Learning for LLMs: A Survey》
  • helm 的常用命令
  • pinia状态管理的作用和意义