五分钟XML速成

原文链接:

XML - Dive Into Python 3

深入探讨

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

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

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

XML 复制代码
<?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 sounds
      bloated, but consider this &amp;mdash; my longest chapter so far
      would 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 to
      question 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 a
      tech talk on video encoding. </summary>
  </entry> 
</feed>

五分钟 XML 速成课程

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

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

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

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

根元素

XML 复制代码
<foo>
  <bar></bar> 
</foo>

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

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

XML 复制代码
<foo></foo> 
<bar></bar>

属性

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

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

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

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

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

文本内容

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

XML 复制代码
<foo lang='en'>
  <bar lang='fr'>PapayaWhip</bar> 
</foo>

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

空元素

XML 复制代码
<foo></foo>

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

XML 复制代码
<foo/>

命名空间

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

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

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

XML 复制代码
<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声明来定义命名空间,并将其与一个前缀相关联。

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

XML 复制代码
<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 复制代码
<?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>是 "文章标题" 还是 "图标标题"。

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

XML 复制代码
<!-- 声明两个命名空间: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元素。

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

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

XML 复制代码
<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 提要中的结构如下所示:

XML 复制代码
<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 far
    would 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()函数:

python 复制代码
>>> 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 中,一个元素的行为类似列表,列表中的项即为该元素的子元素。

python 复制代码
# 承接上一个示例
>>> 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 字典的形式获取。

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 也支持这一操作。

python 复制代码
>>> 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元素,查询格式需稍作调整。
python 复制代码
>>> 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()方法,该方法返回第一个匹配的元素。

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

python 复制代码
>>> 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 也支持查找后代元素(即子元素、孙元素及任意嵌套层级的元素),具体方式如下:

python 复制代码
>>> 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 支持。

相关推荐
余防1 小时前
XXE - 实体注入(xml外部实体注入)
xml·前端·安全·web安全·html
未来之窗软件服务8 小时前
万象EXCEL开发(四)格式解读theme1.xml ——东方仙盟练气期
xml·仙盟创梦ide·东方仙盟·万象excel
未来之窗软件服务1 天前
万象EXCEL开发(二)格式解读sharedStrings.xml——东方仙盟练气期
xml·excel·仙盟创梦ide·东方仙盟·万象excel·东方仙盟格式
goTsHgo2 天前
Spring XML 配置简介
xml·java·spring
半导体守望者2 天前
TR帝尔编码器GSD文件 PROFIBUS XML PROFINET EtherCAT 文件 ADH CDH CMV等
xml·经验分享·笔记·机器人·自动化·制造
极光雨雨2 天前
XML中的 CDATA mybaitis xml中的 <![CDATA[ xxxx ]]>
xml
未来之窗软件服务3 天前
万象EXCEL开发(三)格式解读calcChain.xml——东方仙盟练气期
xml·仙盟创梦ide·东方仙盟·万象excel
MintYouth3 天前
【精】C# 精确判断XML是否存在子节点
xml·c#
qq_318693013 天前
XML 站点地图制作与提交全流程,新站收录效率提升 50%
xml·数据库·mysql
卷Java4 天前
WXML 编译错误修复总结
xml·java·前端·微信小程序·uni-app·webview