XPath 是一種用於定址 XML 檔案部分的語言。它在 XSLT 中使用並且是 XQuery 的子集。這個庫也可用於大多數其他程式語言。
在我前面的幾十篇文章,寫了上百萬字把python的所有基礎已經講得很明白了,不管你是不是小白,跟著學都能學會,同時在我的粉絲群,我還會對教學中的問題進行答疑,所以包教包會的口號,我從來不是吹的。
這裡是我的基礎教學專欄:python全棧基礎詳細教學專欄系列
當然,如果你對qq機器人制作感興趣請檢視專欄:qq機器人制作詳細教學專欄
這兩個專欄,我為什麼放在一起?第一個專欄是基礎教學,第二個專欄是進階,所以你在不會基礎之前,請不要冒然學習機器人制作。
說了半天,我還沒說為什麼寫這一篇的原因,前面的基礎我已經差不多寫完了,基礎不會的自己去看我專欄,上百萬字寫基礎,我已經很用心教大家了。基礎過後,我們即將開始學爬蟲,因此xpath你不得不掌握。認真跟著我學,多看幾天就會了。
其它專欄,看你自己個人興趣,這五個專欄我是主打,並是我強烈推薦。
有任何問題可以粉絲群問我:
如果有一段html如下:
<html>
<body>
<a>link</a>
<div class='container' id='divone'>
<p class='common' id='enclosedone'>Element One</p>
<p class='common' id='enclosedtwo'>Element Two</p>
</div>
</body>
</html>
在整個頁面中查詢具有特定 id 的元素:
//*[@id='divone'] # 返回 <div class='container' id='divone'>
在特定路徑中查詢具有特定 id 的元素:
/html/body/div/p[@id='enclosedone'] # 返回 <p class='common' id='enclosedone'>Element One</p>
選擇具有特定 id 和 class 的元素:
//p[@id='enclosedone' and @class='common'] #返回 <p class='common' id='enclosedone'>Element One</p>
選擇特定元素的文字:
//*[@id='enclosedone']/text() # 返回 Element One
比如有如下xml:
<r>
<e a="1"/>
<f a="2" b="1">Text 1</f>
<f/>
<g>
<i c="2">Text 2</i>
Text 3
<j>Text 4</j>
</g>
</r>
用xpath
/r/e
將選擇此元素:
<e a="1"/>
用xpath:
/r/f/text()
將選擇具有此字串值的文位元組點:
"Text 1"
而這個 XPath:
string(/r/f)
返回同樣是:
"Text 1"
步驟如下:
以我自己的主頁網址為例:
https://blog.csdn.net/weixin_46211269?spm=1000.2115.3001.5343
分析:
鎖定定位為:
user-profile-statistics-num
則xpath寫為:
//div[@class="user-profile-statistics-num"]
以上就是一種簡單的偵錯xpath方法,難的我就不介紹了,沒必要吧,如果大家覺得有必要,評論區留言,人多我就重新編輯補充。
我們使用布林來檢查我們寫的xpath是否存在,布林真是一個很不錯的東西。
這裡我就構造一個xml如下:
<House>
<LivingRoom>
<plant name="rose"/>
</LivingRoom>
<TerraceGarden>
<plant name="passion fruit"/>
<plant name="lily"/>
<plant name="golden duranta"/>
</TerraceGarden>
</House>
使用布林來判斷:
boolean(/House//plant)
輸出:
true
說明該路徑正確。
假設有這樣一個xml:
<Animal>
<legs>4</legs>
<eyes>2</eyes>
<horns>2</horns>
<tail>1</tail>
</Animal>
使用布林判斷:
boolean(/Animal/tusks)
輸出:
false
說明這個路徑是錯的。
語法:
其他用途:
假設我構造這樣一個xml:
<Deborah>
<address>Dark world</address>
<master>Babadi</master>
<ID>#0</ID>
<colour>red</colour>
<side>evil</side>
</Deborah>
用布林判斷:
boolean(/Deborah/master/text())
或者用string判斷:
string(/Deborah/master) != ''
輸出都為:
true
說明文字不為空。
假設我構造這樣一個xml:
<Dobby>
<address>Hogwartz</address>
<master></master>
<colour>wheatish</colour>
<side>all good</side>
</Dobby>
用布林判斷:
boolean(/Dobby/master/text())
或者用string判斷:
string(/Dobby/master) != ''
輸出:
false
說明文字為空。
說一些比較常見的語法:
例如:
//title[@lang=’chuan’]
這就是一個 XPath 規則,它就代表選擇所有名稱為 title,同時屬性 lang 的值為 chuan的節點。
假設有這樣一個xml:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑匹配如下:
/Galaxy/*[@name]
或者:
//*[@name]
輸出:
<CelestialObject name="Earth" type="planet" />
<CelestialObject name="Sun" type="star" />
假設有如下例子:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑:
/Galaxy/*[contains(@name,'Ear')]
值得補充的是,這裡的contains函數就是代表包含的意思,這裡就是查詢Galaxy路徑下,所有name屬性中含有Ear的節點。
如上,我們也可以如下方式匹配:
//*[contains(@name,'Ear')]
雙引號也可以用來代替單引號:
/Galaxy/*[contains(@name, "Ear")]
輸出:
<CelestialObject name="Earth" type="planet" />
假設有xml如下:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑
/Galaxy/*[contains(lower-case(@name),'ear')]
這裡又出現了新的東西,加入 lower-case() 函數就是來保證我們可以包括所有的大小寫情況。
路徑
/Galaxy/*[contains(lower-case(@name),'ear')]
或者
//*[contains(lower-case(@name),'ear')]
或者,使用雙引號中的字串:
//*[contains(lower-case(@name), "ear")]
輸出
<CelestialObject name="Earth" type="planet" />
假設有xml如下:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑
/Galaxy/*[ends-with(lower-case(@type),'tar')]
補充:這裡又出現了新的函數,ends-with就是匹配以xx結尾。
或者
//*[ends-with(lower-case(@type),'tar')]
輸出
<CelestialObject name="Sun" type="star" />
假設有這個xml:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑
/Galaxy/*[starts-with(lower-case(@name),'ear')]
補充:這裡又出現了新的函數,starts-with就是匹配以什麼開頭。
或者
//*[starts-with(lower-case(@name),'ear')]
輸出
<CelestialObject name="Earth" type="planet" />
假設有這個xml:
<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>
路徑
/Galaxy/*[@name='Sun']
補充:這裡就是我開頭說到的,星號代表所有,@用來選取屬性
或者
//*[@name='Sun']
輸出
<CelestialObject name="Sun" type="star" />
假設有xml如下:
<root>
<element foobar="hello_world" />
<element example="this is one!" />
</root>
xpath匹配:
/root/element[@foobar]
返回:
<element foobar="hello_world" />
假設有xml如下:
<root>
<element foobar="hello_world" />
<element example="this is one!" />
</root>
以下 XPath 表示式:
/root/element[@foobar = 'hello_world']
將返回
<element foobar="hello_world" />
也可以使用雙引號:
/root/element[@foobar="hello_world"]
粉絲群:970353786
假設有xml如下:
<root>
<element>hello</element>
<another>
hello
</another>
<example>Hello, <nested> I am an example </nested>.</example>
</root>
以下 XPath 表示式:
//*[text() = 'hello']
將返回<element>hello</element>
元素,但不返回元素。這是因為該<another>
元素包含hello文字周圍的空格。
要同時檢索<element>and <another>
,可以使用:
//*[normalize-space(text()) = 'hello']
補充:這裡又多了新的函數,normalize-space作用就是去除空白的意思。
要查詢包含特定文字的元素,您可以使用該contains函數。以下表示式將返回<example>
元素:
//example[contains(text(), 'Hello')]
如果要查詢跨越多個子/文位元組點的文字,則可以使用.代替text()。.指元素及其子元素的整個文字內容。
例如:
//example[. = 'Hello, I am an example .']
要檢視多個文位元組點,您可以使用:
//example//text()
這將返回:
為了更清楚地看到一個元素的整個文字內容,可以使用該string函數:
string(//example[1])
要不就
string(//example)
依然返回:
Hello, I am an example .
現在我們要補充新的東西,又要開始記住了:
ancestor 選取當前節點的所有先輩(父、祖父等)。
ancestor-or-self 選取當前節點的所有先輩(父、祖父等)以及當前節點本身。
attribute 選取當前節點的所有屬性。
child 選取當前節點的所有子元素。
descendant 選取當前節點的所有後代元素(子、孫等)。
descendant-or-self 選取當前節點的所有後代元素(子、孫等)以及當前節點本身。
following 選取檔案中當前節點的結束標籤之後的所有節點。
namespace 選取當前節點的所有名稱空間節點。
parent 選取當前節點的父節點。
preceding 選取檔案中當前節點的開始標籤之前的所有節點。
preceding-sibling 選取當前節點之前的所有同級節點。
self 選取當前節點。
為什麼我在這裡又來強調一下?因為很重要!
nodename 選取此節點的所有子節點。
/ 從根節點選取。
// 從匹配選擇的當前節點選擇檔案中的節點,而不考慮它們的位置。
. 選取當前節點。
.. 選取當前節點的父節點。
@ 選取屬性。
在下面的表格中,列出了一些路徑表示式以及表示式的結果:
bookstore 選取 bookstore 元素的所有子節點。
/bookstore 選取根元素 bookstore。 註釋:假如路徑起始於正斜槓( / ),則此路徑始終代表到某元素的絕對路徑!
bookstore/book 選取屬於 bookstore 的子元素的所有 book 元素。
//book 選取所有 book 子元素,而不管它們在檔案中的位置。
bookstore//book 選擇屬於 bookstore 元素的後代的所有 book 元素,而不管它們位於 bookstore 之下的什麼位置。
//@lang 選取名為 lang 的所有屬性。
謂語用來查詢某個特定的節點或者包含某個指定的值的節點。謂語被嵌在方括號中。
看一些例子就知道了:
路徑表示式 結果
/bookstore/book[1] 選取屬於 bookstore 子元素的第一個 book 元素。
/bookstore/book[last()] 選取屬於 bookstore 子元素的最後一個 book 元素。
/bookstore/book[last()-1] 選取屬於 bookstore 子元素的倒數第二個 book 元素。
/bookstore/book[position()<3] 選取最前面的兩個屬於 bookstore 元素的子元素的 book 元素。
//title[@lang] 選取所有擁有名為 lang 的屬性的 title 元素。
//title[@lang='eng'] 選取所有 title 元素,且這些元素擁有值為 eng 的 lang 屬性。
/bookstore/book[price>35.00] 選取 bookstore 元素的所有 book 元素,且其中的 price 元素的值須大於 35.00。
/bookstore/book[price>35.00]/title 選取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值須大於 35.00。
XPath 萬用字元可用來選取未知的 XML 元素。
萬用字元 描述
* 匹配任何元素節點。
@* 匹配任何屬性節點。
node() 匹配任何型別的節點。
在下面的表格中,我們列出了一些路徑表示式,以及這些表示式的結果:
路徑表示式 結果
/bookstore/* 選取 bookstore 元素的所有子元素。
//* 選取檔案中的所有元素。
//title[@*] 選取所有帶有屬性的 title 元素。
通過在路徑表示式中使用「|」運運算元,您可以選取若干個路徑。
在下面的表格中,列出了一些路徑表示式,以及這些表示式的結果:
路徑表示式 結果
//book/title | //book/price 選取 book 元素的所有 title 和 price 元素。
//title | //price 選取檔案中的所有 title 和 price 元素。
/bookstore/book/title | //price 選取屬於 bookstore 元素的 book 元素的所有 title 元素,以及檔案中所有的 price 元素。
假設我們有xml如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
其實這些內容,大可不必都掌握,但是你一定要知道,你想用的時候,再來本文查一下會用就行。
這是相關範例:
問題是:這裡提到的祖先,孩子,兄弟,父母節點,大家知道嗎?如果你會html的話,你應該知道。超過2000讚我可以出一篇html的教學,本篇我就暫時預設大家知道了。
假設有xml如下:(這裡已經很形象說明了祖先,孩子,兄弟,父母節點的關係了,仔細看看)
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male"/>
<brother name="Goten" gender="male"/>
</Dad>
</GrandFather>
路徑
//Me/ancestor::node()
輸出:
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
假設有xml如下:
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<brother name="Goten" gender="male" />
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>
路徑:
//Me/following-sibling::brother
輸出:
<brother name="Goten" gender="male" />
假設有xml如下:
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>
路徑
//Me/ancestor::GrandFather
或者
//Me/parent::node()/parent::node()
輸出:
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>
還是假設xml如下:
<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male"/>
<brother name="Goten" gender="male"/>
</Dad>
</GrandFather>
路徑
//Me/ancestor::Dad
或者
//Me/parent::node()
輸出:
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
假設有xml如下:
<Dashavatar>
<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>
<Avatar name="Parashurama"/>
<Avatar name="Rama"/>
<Avatar name="Krishna"/>
<Avatar name="Kalki"/>
</Dashavatar>
路徑
//Avatar[@name='Parashurama']/following-sibling::node()
輸出:
<Avatar name="Rama" />
<Avatar name="Krishna" />
<Avatar name="Kalki" />
假設有xml如下:
<Dashavatar>
<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>
<Avatar name="Parashurama"/>
<Avatar name="Rama"/>
<Avatar name="Krishna"/>
<Avatar name="Kalki"/>
</Dashavatar>
路徑
//Avatar[@name='Parashurama']/preceding-sibling::node()
輸出:
<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>
獲取 House 中的所有房間名為 Room 的孩子。
假設有xml如下:
<House>
<numRooms>4</numRooms>
<Room name="living"/>
<Room name="master bedroom"/>
<Room name="kids' bedroom"/>
<Room name="kitchen"/>
</House>
路徑
/House/child::Room
或者
/House/*[local-name()='Room']
輸出:
<Room name="living" />
<Room name="master bedroom" />
<Room name="kids' bedroom" />
<Room name="kitchen" />
獲得 House 中的所有房間(不考慮位置)。
假設有xml如下:
<House>
<numRooms>4</numRooms>
<Floor number="1">
<Room name="living"/>
<Room name="kitchen"/>
</Floor>
<Floor number="2">
<Room name="master bedroom"/>
<Room name="kids' bedroom"/>
</Floor>
</House>
路徑
/House/descendant::Room
輸出
<Room name="living" />
<Room name="kitchen" />
<Room name="master bedroom" />
<Room name="kids' bedroom" />
我們主要用到count函數,實戰中我們來感悟。
假設有xml如下:
<Goku>
<child name="Gohan"/>
<child name="Goten"/>
</Goku>
路徑
count(/Goku/child)
輸出
2.0
假設有如下xml
<House>
<LivingRoom>
<plant name="rose"/>
</LivingRoom>
<TerraceGarden>
<plant name="passion fruit"/>
<plant name="lily"/>
<plant name="golden duranta"/>
</TerraceGarden>
</House>
路徑
count(/House//plant)
輸出
4.0
假設有xml如下:
<Students>
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
<Student>
<Name>
<First>Bill</First>
<Last>Edwards</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
</Grades>
</Student>
</Students>
選擇至少記錄了 2 個成績的所有學生
//Student[count(./Grades/*) > 1]
輸出
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
假設有xml如下:
<Students>
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
<Student>
<Name>
<First>Bill</First>
<Last>Edwards</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
</Grades>
</Student>
</Students>
選擇所有記錄了 Exam2 分數的學生
//Student[./Grades/Exam2]
或者
//Student[.//Exam2]
輸出
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
語法如下:
1.在特定節點內:
{path-to-parent}/name()='搜尋字串']
2.檔案中的任何位置:
//*[name()='搜尋字串']
假設有xml如下:
<Data>
<BioLight>
<name>Firefly</name>
<model>Insect</model>
</BioLight>
<ArtificialLight>
<name>Fire</name>
<model>Natural element</model>
<source>flint</source>
</ArtificialLight>
<SolarLight>
<name>Sun</name>
<model>Star</model>
<source>helium</source>
</SolarLight>
</Data>
路徑
/Data/*[contains(local-name(),"Light")]
或者
//*[contains(local-name(),"Light")]
輸出:
<BioLight>
<name>Firefly</name>
<model>Insect</model>
</BioLight>
<ArtificialLight>
<name>Fire</name>
<model>Natural element</model>
<source>flint</source>
</ArtificialLight>
<SolarLight>
<name>Sun</name>
<model>Star</model>
<source>helium</source>
</SolarLight>
假設xml如下:
<College>
<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarPlayer>David Perry</StarPlayer>
</FootBall>
<VolleyBall>
<Members>24</Members>
<Coach>Tim Jose</Coach>
<Name>Avengers</Name>
<StarPlayer>Lindsay Rowen</StarPlayer>
</VolleyBall>
<FoosBall>
<Members>22</Members>
<Coach>Rahul Mehra</Coach>
<Name>Playerz</Name>
<StarPlayer>Amanda Ren</StarPlayer>
</FoosBall>
</College>
路徑
/College/*[ends-with(local-name(),"Ball")]
或者
//*[ends-with(local-name(),"Ball")]
輸出:
<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarPlayer>David Perry</StarPlayer>
</FootBall>
<VolleyBall>
<Members>24</Members>
<Coach>Tim Jose</Coach>
<Name>Avengers</Name>
<StarPlayer>Lindsay Rowen</StarPlayer>
</VolleyBall>
<FoosBall>
<Members>22</Members>
<Coach>Rahul Mehra</Coach>
<Name>Playerz</Name>
<StarPlayer>Amanda Ren</StarPlayer>
</FoosBall>
假設xml如下:
<College>
<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarFootballer>David Perry</StarFootballer>
</FootBall>
<Academics>
<Members>100</Members>
<Teacher>Tim Jose</Teacher>
<Class>VII</Class>
<StarPerformer>Lindsay Rowen</StarPerformer>
</Academics>
</College>
路徑
/College/*/*[starts-with(local-name(),"Star")]
或者
//*[starts-with(local-name(),"Star")]
輸出
<StarFootballer>David Perry</StarFootballer>
<StarPerformer>Lindsay Rowen</StarPerformer>
假設xml如下:
<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>
路徑
/Galaxy/*[local-name()='Light' or local-name()='Device' or local-name()='Sensor']
說白了就是多了幾個or而已。
或者
//*[local-name()='Light' or local-name()='Device' or local-name()='Sensor']
輸出
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
假設xml如下:
<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>
路徑
/Galaxy/*[lower-case(local-name())="light"]
或者
//*[lower-case(local-name())="light"]
輸出
<Light>sun</Light>
假設xml如下:
<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>
路徑
/Galaxy/*[lower-case(local-name())="light"]
或者
//*[lower-case(local-name())="light"]
輸出
<Light>sun</Light>
前期已經送出很多基礎書和資料分析書籍,本次送人工智慧的書籍如下:
參與活動方式:
970353786
私聊我(群主),截圖發給我送書僅在於個人心意,以此鼓勵大家學習,只隨機送兩本。這本書還不錯,喜歡的也可以去京東購買。
這位大佬也寫了一篇xpath:十五分鐘掌握python爬蟲XPath庫 感興趣可以對照我的看看。
python全棧基礎專欄我已經講完大部分基礎,現在我們進軍爬蟲,本篇內容希望大家一定掌握。超過2000收藏,我補一篇html網頁基礎。寫了我週末兩個通宵,希望大家這次支援,謝謝。至於我送書活動,貨真價實,僅代表個人心意,鼓勵大家學習。
公眾號 傳送:xpath 即可領取本篇文章的電子版。