XPath 入門指南:輕鬆理解網頁數據抓取

更新日期: 2024 年 1 月 22 日

在這個數據蒐集日漸容易的時代,網頁數據抓取變得越來越重要。

XPath 作為一種強大的查詢語言,可以幫助我們從網頁中精準地提取所需資料。

無論是網站的文字、圖片還是其他任何元素。

但對許多人來說,XPath 似乎有些複雜和難以理解。

本文的目的,就是要打破這種誤解,用最簡單易懂的方式介紹 XPath,使您能夠輕鬆上手並有效利用這一技術。


XPath 基礎概念

在深入探討 XPath 的實際應用之前,先來理解一下它的基本概念。

XPath,全稱為 XML Path Language,是一種用於在 XML 檔案中查詢資訊的語言。

由於 HTML 是 XML 的一種應用,所以 XPath 也可以用於 HTML 檔案,這使得它成為提取網頁數據的強大工具。

它通過「路徑表達式」來挑選 XML 檔案中的「節點」或「節點集」。這些節點可以是元素、屬性、文字等。

例如,如果我們將一個網頁比喻為一棵樹,那麼每一個 HTML 標籤就像是樹上的一個節點。

圖片來源:https://www.conceptdraw.com/solution-park/software-dom-tree

在 XML 和 HTML 中,節點(Node)的概念不僅限於標籤(也就是元素節點),還包括屬性、文字、註釋等其他類型。讓我來詳細解釋:

元素節點(Element Node)

這是最常見的節點類型,對應於 XML 和 HTML 中的標籤。

例如,<div>、<p>、<span> 等都是元素節點。

屬性節點(Attribute Node)

屬性節點對應於元素的屬性。在 HTML 中,如 class=”someClass”、id=”someId” 等屬性都是屬性節點。它們用於提供關於元素的額外資訊。

文字節點(Text Node)

文字節點包含元素內的文字內容。這些是純文字,不包含任何 HTML 標籤。例如,在 <p>Hello World</p> 中,Hello World 是一個文字節點。

註釋節點(Comment Node)

這些節點包含檔案中的註釋。例如,HTML 中的 <!– This is a comment –>。

其他類型的節點

如檔案節點(Document Node)、處理指令節點等,也是 XML 和 HTML 結構中的一部分。

當我們在 XPath 中進行節點挑選時,我們可能針對的是這些不同類型的節點。使用特定的 XPath 函數和語法,我們可以挑選特定類型的節點。

例如使用 text() 挑選文字節點,或使用 @ 挑選屬性節點。

因此,節點在 XML 和 HTML 的上下文中是一個廣義的概念,涵蓋了檔案中的各種不同組件,而不僅僅是標籤元素。

XPath 基本路徑表達式

在 XPath 中,基本的路徑表達式用於定位 XML 或 HTML 檔案中的節點。這些表達式是構建更複雜查詢的基礎。以下是一些基本的 XPath 路徑表達式及其說明:

nodename

挑選所有名為 nodename 的節點。

  • 範例:挑選所有 <title> 節點。
  • XPath 表達式:title
  • 示範網頁結構
<html><head><title>Page Title</title></head><body>...</body></html>
  • 回傳值
<title>Page Title</title>

補充:XPath 的查詢結果通常是節點集合,而不僅是節點內容。當你查詢時,它會返回整個節點,包括標籤和標籤內的內容。

/

在 XPath 中,當 / 出現在表達式開頭時,它表示查詢是從整個檔案的最上方開始的,即從最外層的節點(通常是整個網頁的 <html> 標籤)開始。

當 / 用在兩個節點名稱之間,如 /html/body,它就像是告訴我們「先找到 <html> 節點,然後在它的直接下一層找到 <body> 節點」。

  • 範例:挑選 <html> 節點的所有內容。
  • XPath 表達式:/(或等同於 /html)
  • 示範網頁結構
<html><body>...</body></html>
  • 回傳值
<html><body>...</body></html>
  • 範例:挑選 <html> 以下 <body> 節點的所有內容。
  • XPath 表達式:/html/body
  • 示範網頁結構
<html><body>...</body></html>
  • 回傳值
<body>...</body>

//

在 XPath 中,使用 // 表示我們要找的節點可以位於檔案的任何地方,而不用擔心它具體在哪一層。

簡單來說,// 就像是在整個檔案中進行全面搜索,尋找符合條件的節點。

  • 範例:挑選檔案中所有的 <p> 節點。
  • XPath 表達式://p
  • 示範網頁結構
<html><body><p>Paragraph 1</p><div><p>Paragraph 2</p></div></body></html>
  • 回傳值
<p>Paragraph 1</p>
<p>Paragraph 2</p>

[數字]

在 XPath 表達式中,使用 [數字] 來挑選特定順序的節點,通常用於指定一組節點中的特定一項。

這裡的「特定順序」是指在一組匹配的節點中,按照它們在檔案中出現的順序來,挑選某個特定的節點。

這個數字是基於 1 的索引,意味著 [1] 代表第一個節點,[2] 代表第二個節點,依此類推。

更詳細地解釋如下:

  1. 檔案順序:在 XML 或 HTML 檔案中,節點(如元素、註釋、文字等)是按照特定的順序出現的。這個順序就是它們在檔案中的排列順序,從上到下。
  2. 索引計數:在 XPath 的 [數字] 語法中,數字從 1 開始計數。這意味著 [1] 是指挑選第一個匹配的節點,[2] 是指挑選第二個匹配的節點,以此類推。

舉個例子,假設我們有這樣一個 HTML 片段:

<div>
  <p>段落 1</p>
  <p>段落 2</p>
  <p>段落 3</p>
</div>

如果我們使用 XPath 查詢 //p[2],它將挑選「段落 2」,因為它是按照檔案順序的第二個 <p> 節點。

  • 範例:挑選檔案中第二個 <p> 節點。。
  • XPath 表達式://p[2]
  • 示範網頁結構
<html><body><p>第一段落</p><p>第二段落</p><p>第三段落</p></body></html>
  • 回傳值
<p>第二段落</p>

.

.」代表「當前正在查看的節點」。這意味著當您使用「.」時,它指的是您目前正處於的那個特定節點。

在實際使用中,「.」通常與其他路徑表達式結合使用,以便在特定的上下文中進行更精確的節點挑選。

讓我們通過一個實際的例子來說明 XPath 中「當前節點」的概念:

<html>
  <body>
    <div>
      <p>第一段落</p>
      <p>第二段落</p>
    </div>
    <div>
      <p>第三段落</p>
    </div>
  </body>
</html>

現在,我們要使用 XPath 查詢在第一個 <div> 元素中的所有 <p> 元素。我們的 XPath 查詢可以是這樣的:

首先,挑選第一個 <div> 元素:

  • XPath 表達式:/html/body/div[1]
  • 這個查詢挑選了位於 <body> 下的第一個 <div> 元素。在這一步中,「當前節點」是這個 <div> 元素。

接著,我們想要挑選這個 <div> 元素內的所有 <p> 元素:

  • XPath 表達式:./p
  • 這裡,. 表示「當前節點」,也就是之前查詢中挑選的第一個 <div> 元素。所以,這個查詢是在這個 <div> 內部尋找所有的 <p> 元素
  • 範例:挑選當前節點(假設當前節點是 <div>)中的 <p> 節點。
  • XPath 表達式./p
  • 示範網頁結構
<div><p>Paragraph</p></div>
  • 回傳值
<p>Paragraph in div</p>

沒有上下文的情況下,理解「當前節點」的概念可能會比較困難。在實際應用中,「當前節點」通常是在處理復雜的 XPath 表達式或進行多階段的查詢時才會更加重要和明顯。

因此在撰寫 XPath 表達式時,理解您的查詢如何逐步在檔案結構中前進和變化,非常關鍵。

..

..」在 XPath 表達式中用來挑選任何節點的「父節點」,也就是說,它允許你從當前節點向上移動到其直接上層的節點。

在 XML 或 HTML 的結構中,每個節點(除了根節點)都有一個父節點。父節點是包含當前節點的那個更大的節點。

舉例來說,如果當前節點是一個 <p> 標籤,而這個 <p> 標籤位於一個 <div> 元素內,那麼使用「..」將會挑選這個包含 <p> 的 <div> 元素。

這在你需要根據當前節點的位置來尋找其上層元素時非常有用,特別是當你不確定或不需要知道父節點的具體標籤名稱時。

  • 範例:挑選當前節點的父節點。(假設當前節點是 <p>)
  • XPath 表達式..
  • 示範網頁結構
<html><body><div><p>Paragraph</p></div></body></html>
  • 回傳值
<div><p>Paragraph</p></div>
  • 範例:挑選當前節點的父節點。(假設當前節點是 <p>)
  • XPath 表達式../..
  • 示範網頁結構
<html><body><div><p>Paragraph</p></div></body></html>
  • 回傳值
<div><div><p>Paragraph</p></div></body>

@

在 XPath 表達式中,「@」符號被用來挑選元素的屬性。這允許您直接訪問並操作 XML 或 HTML 元素的屬性值。

它們通常用於定義元素的特性或提供與元素相關的元數據,如 id、class、href 等。

  • 範例:挑選所有有 href 屬性的 <a> 節點。
  • XPath 表達式://a/@href
  • 示範網頁結構
<a href="https://example.com">Link</a>
  • 回傳值
https://example.com

當 / 和 @ 結合使用時(如 //a/@href),這裡的 / 不再是表示尋找「元素子節點」,而是代表尋找「屬性節點」。

這裡的 //a 部分找到所有的 <a> 元素節點,接著 @href 挑選這些 <a> 節點的 href 屬性值。

這裡需要提醒,由於屬性使採用屬性 – 屬性值」的架構。

當我們使用 XPath 查詢如 //a/@href 時,雖然我們說「選擇屬性 @href」,但實際返回的是該屬性的文本值(在這個例子中是 https://example.com)。

它與前述提及「元素查詢」的回傳值不同,並非回傳元素的子節點(包含開始和結束標籤)。

[ ]

在 XPath 中,[ ] 用於在路徑表達式中的篩選條件,它允許您根據特定條件來挑選節點。

這些條件可以是節點擁有的特定屬性、包含的文字內容,或者符合其他一些規則。

  • 範例:挑選所有包含特定子元素 <span> 的 <div> 節點。
  • XPath 表達式://div[span]
  • 示範網頁結構
<html><body><div><span>示例文字</span></div></body></html>
  • 回傳值
<div><span>示例文字</span></div>

當我們說「尋找符合特定條件的 <div> 元素」,這個條件是指 <div> 元素必須「直接包含至少一個 <span> 子元素」。

在表達式 //div[span] 中,[span] 是一個條件表達式,它應用於每個挑選的 <div> 元素。這個條件不是用來挑選 <span> 元素本身,而是用來檢查每個 <div> 是否滿足「有 <span> 子元素」這一標準。

這意味著表達式挑選的是 <div> 元素,而不是 <span> 元素。只有那些包含至少一個 <span> 子元素的 <div> 元素會被選中。

//div[span] 與 //div/span 的差異是什麼?

//div[span] 的意涵

這個表達式尋找所有直接包含至少一個 <span> 子元素的 <div> 元素。[span] 是一個條件表達式,用於篩選 <div> 元素。

重點在於條件 [span]:它不是在挑選 <span> 元素,而是在挑選那些符合條件(即包含至少一個 <span> 子元素)的 <div> 元素。

結果是 <div> 元素本身,而不是 <span> 元素。

//div/span 的意涵

這個表達式尋找所有位於 <div> 元素內部的 <span> 元素。這裡的 / 表示層級關係,指示 XPath 引擎尋找 <div> 的所有子元素 <span>。

重點在於層級 /:它是在挑選位於 <div> 元素內部的所有 <span> 元素。

結果是 <span> 元素本身,而不是包含它們的 <div> 元素。

假設我們有以下的 HTML 檔案結構:

<html>
  <body>
    <div>
      <span>第一個 div 的 span</span>
    </div>
    <div>
      <p>第二個 div 的 p 元素,內有 <span>span</span></p>
    </div>
    <div>
      第三個 div,沒有 span
    </div>
  </body>
</html>

//div[span] 匹配結果

<div>
  <span>第一個 div 的 span</span>
</div>

//div/span 匹配結果:

<span>第一個 div 的 span</span>
<span>span</span>
  • 範例:挑選所有 class 屬性為 “example” 的 <div> 節點。
  • XPath 表達式://div[@class=’example’]
  • 示範網頁結構
<html><body><div class='example'>...</div><div>...</div></body></html>
  • 回傳值
<div class='example'>...</div>

@ 符號在不同使用情境,會有不同的使用差異:

@ 用於挑選屬性

在 XPath 中,當 @ 單獨出現時(如 //box/@class),它是表是直接挑選元素的特定屬性。

這裡,//box/@class 表達式會挑選所有 <box> 元素的 class 屬性。

@ 用於篩選條件

當 @ 出現在方括號 [ ] 內時,它的作用有所不同。在這裡,@ 用於指定一個條件,即方括號內的篩遠條件。

例如,在表達式 //box[@class=’red’] 中,[@class=’red’] 是一個條件,意思是「挑選那些其 class 屬性等於 ‘red’ 的 <box> 元素」。

總結來說,@ 在不同的上下文中有不同的用途。當它在 [] 內時,它用於設定篩選條件;而當它單獨與 / 結合時,則用於直接挑選屬性。

當然可以。以下是「XPath 進階路徑表達式」的內容,這些表達式允許您進行更複雜和精確的節點挑選:

XPath 進階路徑表達式

文字節點 text()

text() 用於挑選元素的文字內容。這在你需要提取特定元素中的文字時非常有用。

  • 範例:挑選所有 <p> 元素中的文字。
  • XPath 表達式://p/text()
  • 示範網頁結構
<html>
  <body>
    <p>這是第一個段落的文字。</p>
    <p>這是第二個段落的文字,包含 <span>內嵌元素</span>文字。</p>
    <p>第三個段落的文字。</p>
  </body>
</html>
  • 回傳值
這是第一個段落的文字
這是第二個段落的文字,包含 " 和 " 文字。
第三個段落的文字。

在這個例子中,//p/text() 表達式專門挑選每個 <p> 元素的直接文字內容,但不涉及其任何子元素(如 <span>)的文字。

這代表,即使 <span> 元素包含在 <p> 元素內,它的文字也不會被這個 XPath 表達式選中。

注意,這裡的 / 表示從 <p> 元素(作為節點)轉移到它的文字內容(作為節點的子層)

包含特定文字的節點 contains()

contains() 函數用於挑選包含特定文字的節點。這對於基於文字內容進行挑選特別有用。

  • 範例:挑選包含文字 「Hello」 的 <p> 元素。
  • XPath 表達式://p[contains(text(), ‘Hello’)]
  • 示範網頁結構
<html>
  <body>
    <p>Hello World</p>
    <p>這是一個範例</p>
    <p>另一段包含 Hello 的文字</p>
    <p>完全不相關的段落</p>
  </body>
</html>
  • 回傳值
<p>Hello World</p>
<p>另一段包含 Hello 的文字</p>

屬性值的精確匹配 @attributename=’value’

使用 @ 符號配合屬性值,可以精確匹配具有特定屬性值的元素。

  • 範例:挑選 class 屬性值為 「example」 的所有元素。
  • XPath 表達式://*[@class=’example’]
  • 示範網頁結構
<html>
  <body>
    <div class="example">這是一個 class 為 example 的 div 元素。</div>
    <p class="example">這是一個 class 為 example 的 p 元素。</p>
    <span>這個 span 元素的 class 不是 example。</span>
    <ul>
      <li class="example">列表項目 1</li>
      <li>列表項目 2</li>
    </ul>
  </body>
</html>
  • 回傳值
<div class="example">這是一個 class 為 example 的 div 元素。</div>
<p class="example">這是一個 class 為 example 的 p 元素。</p>
<li class="example">列表項目 1</li>

* 的意義

在 XPath 中,* 代表「任何元素節點」。無論元素的標籤名是什麼,只要它是一個元素節點,* 都會匹配它。

因此,當 * 被用在表達式 //*[@class='example'] 中時:* 挑選檔案中的所有元素節點。

[@class=’example’] 是一個條件,它進一步篩選這些元素,只挑選那些 class 屬性值為 「example」 的元素。

使用邏輯運算符 and、or

XPath 支持使用邏輯運算符 and 和 or 來組合多個條件。

  • 範例:挑選所有 class 屬性值為 「example」 且包含文字 「Hello」 的 <p> 元素。
  • XPath 表達式://p[@class=’example’ and contains(text(), ‘Hello’)]
  • 示範網頁結構
<html>
  <body>
    <p class="example">Hello World</p>
    <p class="example">這是一個範例</p>
    <p class="example">Hello Again</p>
    <p class="other">Hello World</p>
    <div class="example">Hello Div</div>
  </body>
</html>
  • 回傳值
<p class="example">Hello World</p>
<p class="example">Hello Again</p>

子節點位置 position()

position() 函數用於挑選特定位置的子節點。這對於挑選第 N 個特定類型的子節點非常有用。

  • 範例:挑選第二個 <li> 子節點。
  • XPath 表達式://ul/li[position()=2]
  • 示範網頁結構
<html>
  <body>
    <ul>
      <li>列表項目 1</li>
      <li>列表項目 2</li>
      <li>列表項目 3</li>
    </ul>
    <ul>
      <li>另一個列表項目 1</li>
      <li>另一個列表項目 2</li>
    </ul>
  </body>
</html>
  • 回傳值
<li>列表項目 2</li>
<li>另一個列表項目 2</li>

XPath 工具和資源

學習和應用 XPath 時,有一些工具和資源可以幫助您更有效地掌握這項技術。

線上 XPath 測試工具

有許多網站提供免費的 XPath 測試工具,允許您在瀏覽器中直接測試 XPath 查詢。

這些工具通常支持即時驗證,讓您快速檢查和調整您的 XPath 表達式。

例如,「XPather」和「FreeFormatter XPath Tester」是受歡迎的選擇。

開發者工具

大多數瀏覽器的開發者工具(如 Chrome DevTools 或 Firefox Developer Tools)內置了對 XPath 的支持。

您可以使用這些工具來檢查網頁的 DOM 結構,並在控制台中測試 XPath 查詢。

瀏覽器擴展和插件

多數現代瀏覽器都有支持 XPath 查詢的擴展或插件,如 Chrome 的「XPath Helper」和 Firefox 的「XPath Finder」。

這些工具允許您在實際的網頁上直接運行 XPath 查詢,並查看匹配的元素。

常見問題和錯誤處理

在學習和應用 XPath 的過程中,可能會遇到一些常見問題和錯誤。這裡我們將探討一些典型的問題,並提供相應的解決方案:

無法匹配任何節點

當您的 XPath 查詢未返回任何結果時,首先檢查查詢語法是否正確。確認節點路徑、屬性名稱和函數拼寫無誤。

同時,檢查檔案的命名空間。有些 XML 檔案使用命名空間,這可能需要在 XPath 查詢中進行特別處理。

挑選了錯誤的節點

如果查詢挑選了不符合需求的節點,請檢查您的挑選條件。

使用更精確的路徑或更嚴格的條件可以幫助改進查詢。

使用開發者工具檢查網頁的 DOM 結構,確保您的 XPath 查詢與目標網頁的結構相匹配。

處理動態加載的內容

對於動態生成的網頁內容,僅使用 XPath 可能無法獲取數據。

考慮使用像 Selenium 這樣的工具,它可以與瀏覽器的 JavaScript 互動,並等待內容被動態加載。

XPath 表達式性能問題

使用過於複雜或非特定的 XPath 表達式(如使用大量的 // 運算符)可能會導致性能問題。

嘗試調整您的查詢,使其更加具體和高效。

瞭解不同瀏覽器的差異

不同的瀏覽器可能對 XPath 的支持略有不同。在開發跨瀏覽器應用時,請測試您的 XPath 查詢在主流瀏覽器中的行為。

總結

本文我們從基本概念到複雜查詢,試圖涵蓋 XPath 的各個方面。

希望這篇指南能成為您學習和實踐 XPath 旅程中的有益資源。

Similar Posts