五分鐘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://www.pswp.cn/diannao/98085.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98085.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98085.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何直接訪問docker容器中的端口服務而不需要改端口映射

查看docker容器對于宿主服務器的ip地址 docker inspect -f {{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}} $容器名 # 替換$容器名 為自己的啟動docker內的服務&#xff0c;監聽端口是否用信息 curl http://172.17.0.2:90有信息就可以直接通過該ip訪問docker容器端口…

《Istio故障溯源:從流量劫持異常到服務網格的底層博弈》

服務網格常被企業視為微服務通信復雜性的“終極方案”。不少團隊在部署Istio時,往往滿足于“控制面啟動、Sidecar注入成功”的表層驗證,卻忽視了底層機制與業務場景的深度適配—這種“重部署輕調優”的心態,往往為后續的生產故障埋下隱患。某大型金融機構的核心交易中臺在接…

第24節:3D音頻與空間音效實現

第24節&#xff1a;3D音頻與空間音效實現 概述 3D音頻是構建沉浸式體驗的關鍵組件&#xff0c;它通過模擬真實世界中的聲音傳播特性&#xff0c;為用戶提供空間感知和方向感。本節將深入探討Web Audio API與Three.js的集成&#xff0c;涵蓋空間音效原理、音頻可視化、多聲道處理…

一步搞清楚本地客戶端和全局服務器是如何更新模型的

我們可以把它想象成一個 “老師”和“學生” 協作學習的過程。全局服務器 “老師”本地客戶端 “學生”整個模型更新的過程遵循一個核心原則&#xff1a;“數據不動&#xff0c;模型動”。原始數據永遠留在本地客戶端&#xff0c;只有模型的參數&#xff08;即模型的“知識”…

跨平臺超低延遲RTSP播放器技術設計探究

摘要 RTSP 播放在實驗室里“跑起來”并不難&#xff0c;難的是在真實場景中做到 超低延遲、跨平臺、高穩定&#xff0c;并長期可靠運行。大牛直播SDK&#xff08;SmartMediaKit&#xff09;的全自研跨平臺 RTSP 播放棧&#xff0c;正是把這些工程難題轉化為可用、可控、可交付的…

知識點匯集——web(三)

1.index.php 的備份文件名通常為index.php.bak 2.PHP2是服務器端腳本語言&#xff0c;主要用于處理和生成網頁的內容&#xff0c;當用戶訪問一個網站時&#xff0c;PHP腳本會在服務器上執行&#xff0c;生成動態的HTML頁面&#xff0c;然后將頁面發送給用戶的瀏覽器進行顯示。p…

變頻器【簡易PLC】功能中的時間問題

一、變頻器的簡易PLC功能簡易PLC功能是將提前設置好的多端速頻率&#xff0c;進行自動運行&#xff0c;類似于PLC程序中的CASE指令一樣&#xff0c;我們需要提前設置好幾段頻率&#xff0c;該頻率所維持的時間&#xff0c;以及加減速時間&#xff0c;按下啟動后&#xff0c;變頻…

Swift 解題:LeetCode 372 超級次方(Super Pow)

文章目錄摘要描述題解答案題解代碼分析代碼解析示例測試及結果時間復雜度空間復雜度總結摘要 在算法題里&#xff0c;有一些問題看似“簡單”&#xff0c;比如算一個冪次方&#xff0c;但一旦放大規模就完全不同了。LeetCode 372 超級次方就是這樣的題目。普通的冪運算沒什么難…

揭秘23種設計模式的藝術與技巧之結構型

結構型模式&#xff1a;優化軟件結構的策略代理模式&#xff08;Proxy Pattern&#xff09;代理模式就像一個經紀人&#xff0c;代表真實對象進行操作。比如&#xff0c;在網絡訪問中&#xff0c;我們可能會通過代理服務器來訪問外部網站。在軟件中&#xff0c;當一個對象由于某…

PyTorch圖像數據轉換為張量(Tensor)并進行歸一化的標準操作

transform ToTensor() 是 PyTorch 中用于將圖像數據轉換為張量&#xff08;Tensor&#xff09;并進行歸一化的標準操作&#xff0c;以下是對其功能的逐層解析及關鍵細節&#xff1a;核心功能總結功能描述類型轉換將 PIL Image / numpy 數組 → PyTorch Tensor (dtype: torch.f…

HarmonyOS學習

一&#xff0c;DevEoc Studio基本內容學習項目工程目錄entry 默認的項目入口模塊ets 界面相關文件&#xff08;目前都放入pages文件內即可&#xff09;resource資源文件&#xff0c;配置文件index.est默認文件’ ‘開頭的一般為裝飾器&#xff0c;修飾功能&#xff0c;來約定后…

【大前端】Vue 和 React 主要區別

Vue 與 React 的主要區別 在前端開發領域&#xff0c;Vue 和 React 是兩大最受歡迎的框架/庫。盡管它們都可以幫助我們構建現代化的 Web 應用&#xff0c;但在設計理念、開發方式、生態系統等方面有許多不同。本文將從多個角度對兩者進行對比。 目錄 框架與庫的定位核心理念…

高級RAG策略學習(五)——llama_index實現上下文窗口增強檢索RAG

LlamaIndex上下文窗口實現詳解 概述 本文檔詳細講解基于LlamaIndex框架實現的上下文窗口RAG系統&#xff0c;重點分析關鍵步驟、語法結構和參數配置。 1. 核心導入與環境配置 1.1 必要模塊導入 from llama_index.core import Settings from llama_index.llms.dashscope import …

Doris 數據倉庫例子

基于 Apache Doris 構建數據倉庫的方案和具體例子。Doris 以其高性能、易用性和實時能力&#xff0c;成為構建現代化數據倉庫&#xff08;特別是 OLAP 場景&#xff09;的優秀選擇。一、為什么選擇 Doris 構建數據倉庫&#xff1f;Doris&#xff08;原名 Palo&#xff09;是一個…

WebRTC進階--WebRTC錯誤Failed to unprotect SRTP packet, err=9

文章目錄 原因分析 SRTP Anti-Replay 機制 客戶端源碼 err=9 的定義: 為什么會觸發 replay_fail ? 解決方向 原因分析 SRTP Anti-Replay 機制 SRTP 收包時會用一個 Replay Window(64/128個序列號大小)檢查 seq 是否合理。 如果你構造的恢復包 recover_seq 比當前接收窗口…

Web服務與Nginx詳解

文章目錄前言一、Web 概念1.1 Web 的基本概念1.1.1 特點1.2 B/S 架構模型1.3 Web 請求與響應過程1.4 靜態資源與動態資源1.5 Web 的發展階段1.6 實驗&#xff1a;搭建最小 Web 服務1.6.1 實驗目標1.6.2 實驗步驟1.7 小結二、HTTP 與 HTTPS 協議2.1 HTTP 與 HTTPS 的區別2.2 HTT…

CC-Link IE FB 轉 DeviceNet 實現歐姆龍 PLC 與松下機器人在 SMT 生產線錫膏印刷環節的精準定位控制

案例背景在電子制造行業&#xff0c;SMT&#xff08;表面貼裝技術&#xff09;生產線對設備的精準控制要求極高。某電子制造企業的 SMT 生產線中&#xff0c;錫膏印刷機、SPI&#xff08;錫膏厚度檢測儀&#xff09;等前段設備采用了基于 CC-Link IE FB 主站的歐姆龍 NJ 系列 P…

IP5326_BZ 支持C同口輸入輸出的移動電源芯片 2.4A的充放電電流 支持4LED指示燈

IP5326 是一款集成升壓轉換器、鋰電池充電管理、電池電量指示的多功能電源管理 SOC&#xff0c;為移動電源提供完整的電源解決方案。得益于 IP5326 的高集成度與豐富功能,使其在應用時僅需極少的外圍器件&#xff0c;并有效減小整體方案的尺寸&#xff0c;降低 BOM 成本。IP532…

若依基礎學習

若依基礎學習 1.修改數據庫密碼以及連接名&#xff1a; RuoYi-Vue-master\ruoyi-admin\src\main\resources\application-druid.yml2.各個文件作用&#xff1a; ruoyi-admin (主啟動)├── ruoyi-framework (框架核心)│ ├── ruoyi-common (通用工具)│ └── ruoyi-sy…

靶向肽Dcpep

名稱&#xff1a;靶向肽Dcpep三字母序列&#xff1a;NH2-Phe-Tyr-Pro-Ser-Tyr-His-Ser-Thr-Pro-Gln-Arg-Pro-OH單字母序列&#xff1a;NH2-FYPSYHSTPQRP-OH分子式&#xff1a;C69H94N18O19分子量&#xff1a;1479.62備注&#xff1a;僅供科研&#xff0c;不用于人體簡述&#x…