Xpath解析及其語法

ihav2carryon發表於2024-12-04

XPath解析

XPath(XML Path Language)是一種用於在XML和HTML文件中查詢資訊的語言,其透過路徑表示式來定位節點,屬性和文字內容,並支援複雜查詢條件,XPath 是許多 Web 抓取工具如 Scrapy,Selenium 等的核心技術之一

XPath 解析的基本步驟

  1. 匯入lxml.etree

    from lxml import etree
    
  2. 使用etree.parse(filename, parser=None)函式返回一個樹形結構

    • etree.parse()用於解析本地XML或HTML檔案,並將其轉換為一個樹形結構即ElementTree物件,可以透過該物件訪問文件的各個節點
    • filename:要解析的檔案路徑
    • parser(可選):預設情況下,parser()會根據副檔名自動選擇合適的解析器,如.xml檔案使用XML解析器,.html使用HTML解析器
  3. 使用etree.HTML(html_string, parser=None)解析網路html字串

    • html_string :要解析的HTML字串
    • parser:(可選):預設情況下etree.HTML()使用etree.HTMLparser()進行解析
    • 返回值:etree.HTML()返回一個ELement物件,表示HTML文件的根元素,可以透過該物件訪問文件各個節點
  4. 使用.xpath(xpath_expression)在已經解析好的HTML文件中執行XPath查詢

    result = html_tree.xpath(xpath_expression)
    
    • xpath_expression:XPath表示式,用於在文件中查詢節點,XPath表示式可以是絕對路徑或相對路徑,也可以包含謂詞,函式和軸操作,主要的XPath語法下面會展開講解
    • html_tree:可以是 ElementTree 物件(由 etree.parse() 返回)或 Element 物件(由 etree.HTML() 返回)
from lxml import etree

# 使用etree.parser()解析檔案路徑
parser = etree.HTMLParser(encoding='utf-8')  # 以utf8進行編碼
tree = etree.parse('../Learning02/三國演義.html', parser=parser)
print(tree)
#output-> <lxml.etree._ElementTree object at 0x000001A240107000>

# 使用etree.HTML()解析本地檔案或網路動態HTML
# 讀取檔案 解析為字串
file = open('../Learning02/三國演義.html', 'r', encoding='utf-8')
data = file.read()
root = etree.HTML(data)
print(root)

#整合
root = etree.HTML(open('../Learning02/三國演義.html', 'r', encoding='utf-8').read())
print(root)
#output-> <Element html at 0x1a23e462880>

XPath語法

XPath語法可以用於在XML與HTML文件中查詢資訊的語言

路徑表示式

XPath使用路徑表示式來定位文件中的節點,路徑也可以分為絕對路徑與相對路徑

絕對路徑

  • /:表示從根節點開始選擇,其用於定義一個絕對路徑

從根節點html開始查詢到head,再從head下找出title標籤

root = etree.HTML(open('../Learning02/三國演義.html', 'r', encoding='utf-8').read())
all_titles = root.xpath('/html/head/title')
for title in all_titles:
    print(etree.tostring(title, encoding='utf-8').decode('utf-8'))
#output-> <title>《三國演義》全集線上閱讀_史書典籍_詩詞名句網</title>

相對路徑

相比與絕對路徑,相對路徑使用率更好,更好用

  • //:表示從當前節點開始,選擇文件中所有符合條件的節點,並且不考慮他們的位置
root = etree.HTML(open('../Learning02/三國演義.html', 'r', encoding='utf-8').read())
all_a = root.xpath('//a')
for a in all_a:
    print(a.text)
#None
#首頁
#分類
#作者
#...

當前節點

  • ./:表示當前節點,通常用於指明當前節點本身,避免混淆
all_a = root.xpath('//a')
print(all_a[1].xpath('./text()')) #./表示當前的a標籤
#output-> ['首頁']

選擇屬性

  • @:用於選擇元素的屬性,而不是元素本身
# 使用 @ 選擇 <a> 標籤的 href 屬性
all_hrefs = root.xpath('//a[@href]')
for hrefs in all_hrefs:
    print(etree.tostring(hrefs, encoding='unicode'))

XPath謂語

謂語是xpath中用於進一步篩選節點的表示式,通常放在方括號[] 內,其可以基於節點的位置,屬性值,文字內容或其他條件來選擇特定的節點,謂語可以巢狀使用,也可以與其他謂語組合使用

  • 基本語法

    //element[condition]
    
    • element:要選擇的元素
    • condition:謂語中的條件,用於進一步篩選符合條件的元素

位置謂語

位置謂語用於根據節點在兄弟節點中的位置進行選擇,可以使用position()或直接指定位置編號

  • 獲取第一個ul標籤中的第一個li標籤

    #//ul獲取的是所有ul,[0]選擇第一個
    lis = root.xpath('//ul')[0].xpath('./li[1]')
    for li in lis:
        print(etree.tostring(li, encoding='unicode'))
    #output-> <li><a href="/">首頁</a></li>
    
  • 使用last()獲取最後第一個節點,和導數第二個節點

    # 倒一個
    last_li = root.xpath('//ul')[0].xpath('./li[last()]')
    print(etree.tostring(last_li[0], encoding='unicode'))
    # 倒二個
    last_second_li = root.xpath('//ul')[0].xpath('./li[last()-1]')
    print(etree.tostring(last_second_li[0], encoding='unicode'))
    #output-> <li><a href="/app/">安卓下載</a></li>
    #<li><a href="/book/">古籍</a></li>
    
  • 使用position()獲取位置進行篩選

    # 獲取前兩個li標籤
    last_li = root.xpath('//ul')[0].xpath('./li[position()<3]')
    for li in last_li:
        print(etree.tostring(li, encoding='unicode'))
    # 獲取偶數位標籤
    lis = root.xpath('//ul')[0].xpath('./li[position() mod 2=0]')
    for li in lis:
        print(etree.tostring(li, encoding='unicode'))
    
  • 屬性謂語

    屬性謂語用於選擇具體特定屬性的節點

    • 使用@attribute來獲取屬性名稱,結合條件進行篩選
    # 選取所有具有 href 屬性的 a 元素
    hrefs = root.xpath("//a[@href]")
    for href in hrefs:
        print(etree.tostring(href, encoding='unicode'))
    
    • 查詢class屬性值
    all_class = root.xpath('//@class')
    print(all_class)
    
  • 組合謂語

    將多個條件組合在一起,使用邏輯運算子 and,or等來建立更復雜的謂語

    #選取href屬性值為https://example.com且class屬性值為link的a元素
    //a[@href='https://example.com' and @class='link']
    
    #選取href屬性值為https://example.com或https://another.com的a 元素
    //a[@href='https://example.com' or @href='https://another.com']
    
  • 函式謂語

    Xpath提供了許多內建函式,來應對更復雜的篩選條件

    • contains((string1, string2)函式:

      • string1:要搜尋的字串
      • string2:要查詢的字串
      # 選取class包含"book"的img標籤
      images = root.xpath('//img[contains(@src,"book")]')
      for image in images:
          print(etree.tostring(image, encoding='unicode'))
      
    • starts-with(string1, string2)函式:

      檢查一個字串是否以指定字元的字首開始,是返回true,否返回false

      • string1:要檢查的字串
      • string2:作為字首的字串
      # 選取所有href以https://開頭的a標籤
      all_a = root.xpath('//a[starts-with(@href,"https:")]')
      for a in all_a:
          print(etree.tostring(a, encoding='unicode'))
      
  • 文字內容謂語

    用於選擇包含特定文字內容的節點,可以使用text()函式來提取節點的文字內容

    # 選擇使用包含"三國"文字的p標籤
    paragraphs = root.xpath('//p[contains(text(),"三國")]')
    for p in paragraphs:
        print(etree.tostring(p, encoding='unicode'))
    

萬用字元

xpath提供了多種萬用字元,用於在路徑表示式中匹配未知的元素,屬性,或任何節點.這些萬用字元非常有用,尤其是當不確定具體節點名稱和結構的情況下

萬用字元 描述
* 匹配任何元素節點。 一般用於瀏覽器copy xpath會出現
@* 匹配任何屬性節點。
node() 匹配任何型別的節點。

使用*匹配任何元素節點

  • * 是最常用的萬用字元之一,其可以匹配任何元素,而不需要具體標籤名.這在不確定元素名稱或希望選擇所有型別的元素時非常有用
# 選擇所有 div 下的所有子元素
divs = root.xpath("//div/*")
for div in divs:
    print(etree.tostring(div, encoding='unicode'))

使用@* 匹配任何屬性節點

  • @* 用於匹配任何屬性節點,而不用指定具體屬性名稱,在你不確定屬性名稱或希望選擇所有屬性時非常有用
# 選擇所有 a 元素的所有屬性
all_a = root.xpath('//a/@*')
for a in all_a:
    print(a)

使用node()匹配任何型別的節點

  • node() 是一個更通用的萬用字元,其能匹配任何型別節點,包括元素節點,文字節點,屬性節點,註釋節點等等,其在需要選擇不僅僅是元素節點是十分有用
# 選擇所有 ul 下的所有子節點(包括文字節點)
nodes = root.xpath('//ul/node()')
print(nodes)
#output-> ['\n ', <Element li at 0x2009621d800>, '\n,...] 

XPath,re正則,BeautifulSoup對比

在之前的學習中我們首先學習了re正規表示式,其次學習了更加便捷的bs4,哪為何還要學習XPath解析呢,接下來我們將它們的優點和適用場景進行對比學習

工具 優點 缺點 適用場景
XPath 強大的路徑表達能力,支援層級結構和條件查詢 學習曲線較陡,對不規範 HTML 容錯性較差 結構化良好的 XML/HTML,複雜查詢
re 靈活性高,適合處理純文字中的模式匹配 不適合解析 HTML/XML,可讀性差 從純文字中提取特定模式的資料
BeautifulSoup 易於使用,容錯性強,適合初學者 效能稍低,功能有限 不規範的 HTML,簡單資料提取,網頁抓取
  • 總結
    • 若需要處理結構良好的XML或HTML文件,並需要進行復雜查詢,那麼XPath解析是最佳選擇
    • 若需要從純文字中提取特定模式的資料時,如從日誌中提取日期,IP地址的,re正規表示式是最佳選擇
    • 需要解析不規範的 HTML 或者只需要進行簡單的資料提取,BeautifulSoup 是最友好的選擇

相關文章