
.## 一、XPath 簡介XPathXML Path Language是一種在 XML 文檔中查找信息的語言它使用路徑表達(dá)式來選取 XML 文檔中的節(jié)點(diǎn)或節(jié)點(diǎn)集。XPath 最初是為 XSLT 和 XPointer 設(shè)計的核心組件但如今已被廣泛應(yīng)用于Web 爬蟲Python 的 lxml、Scrapy 框架自動化測試Selenium、Appium 元素定位數(shù)據(jù)提取從 HTML/XML 中解析結(jié)構(gòu)化數(shù)據(jù)配置文件XML 配置的動態(tài)查詢XPath 1.0 是最廣泛支持的版本本文所有語法均基于 XPath 1.0。二、XPath 基本概念2.1 節(jié)點(diǎn)類型XPath 將 XML 文檔抽象為七種節(jié)點(diǎn)類型節(jié)點(diǎn)類型說明示例元素節(jié)點(diǎn)ElementXML 標(biāo)簽book屬性節(jié)點(diǎn)Attribute標(biāo)簽屬性id1文本節(jié)點(diǎn)Text標(biāo)簽內(nèi)文本Hello World注釋節(jié)點(diǎn)CommentXML 注釋!-- comment --處理指令節(jié)點(diǎn)PI?xml ...?命名空間節(jié)點(diǎn)Namespacexmlns:xsl根節(jié)點(diǎn)Root文檔根不可見/2.2 節(jié)點(diǎn)關(guān)系父節(jié)點(diǎn)Parent每個元素和屬性都有且僅有一個父節(jié)點(diǎn)子節(jié)點(diǎn)Children元素節(jié)點(diǎn)可以有零個或多個子元素兄弟節(jié)點(diǎn)Siblings擁有相同父節(jié)點(diǎn)的節(jié)點(diǎn)祖先節(jié)點(diǎn)Ancestor父節(jié)點(diǎn)、祖父節(jié)點(diǎn)等后代節(jié)點(diǎn)Descendant子節(jié)點(diǎn)、孫子節(jié)點(diǎn)等三、XPath 路徑表達(dá)式3.1 基本語法XPath 使用類似文件系統(tǒng)的路徑表達(dá)式來定位節(jié)點(diǎn)/ # 從根節(jié)點(diǎn)開始絕對路徑 // # 從當(dāng)前節(jié)點(diǎn)的后代中選取相對路徑 . # 當(dāng)前節(jié)點(diǎn) .. # 當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) # 選取屬性 * # 通配符匹配任意元素節(jié)點(diǎn) * # 通配符匹配任意屬性節(jié)點(diǎn) node() # 匹配任意類型節(jié)點(diǎn) text() # 匹配文本節(jié)點(diǎn) comment() # 匹配注釋節(jié)點(diǎn)3.2 常用路徑示例假設(shè)有如下 XML 文檔bookstorebookcategorycookingtitlelangenEveryday Italian/titleauthorGiada De Laurentiis/authoryear2005/yearprice30.00/price/bookbookcategorychildrentitlelangenHarry Potter/titleauthorJ K. Rowling/authoryear2005/yearprice29.99/price/book/bookstore表達(dá)式結(jié)果bookstore選取 bookstore 元素的所有子節(jié)點(diǎn)/bookstore選取根元素 bookstorebookstore/book選取 bookstore 下所有 book 子元素//book選取所有 book 元素?zé)o論位置bookstore//book選取 bookstore 后代中所有 book//lang選取所有名為 lang 的屬性/bookstore/book[1]第一個 book 子元素/bookstore/book[last()]最后一個 book 子元素/bookstore/book[position()3]前兩個 book//title[lang]所有帶 lang 屬性的 title//title[langen]lang 屬性為 en 的 title/bookstore/book[price30]price 大于 30 的 book四、XPath 軸Axes軸定義了相對于當(dāng)前節(jié)點(diǎn)的節(jié)點(diǎn)集是 XPath 強(qiáng)大定位能力的核心。4.1 軸的語法軸名稱::節(jié)點(diǎn)測試[謂詞]4.2 常用軸一覽軸名稱縮寫說明ancestor當(dāng)前節(jié)點(diǎn)的所有祖先父、祖父…ancestor-or-self祖先及自身attribute當(dāng)前節(jié)點(diǎn)的所有屬性child默認(rèn)當(dāng)前節(jié)點(diǎn)的所有子元素descendant//當(dāng)前節(jié)點(diǎn)的所有后代descendant-or-self后代及自身following文檔中當(dāng)前節(jié)點(diǎn)結(jié)束標(biāo)簽之后的所有節(jié)點(diǎn)following-sibling當(dāng)前節(jié)點(diǎn)之后的所有兄弟節(jié)點(diǎn)namespace當(dāng)前節(jié)點(diǎn)的所有命名空間節(jié)點(diǎn)parent..當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)preceding文檔中當(dāng)前節(jié)點(diǎn)開始標(biāo)簽之前的所有節(jié)點(diǎn)preceding-sibling當(dāng)前節(jié)點(diǎn)之前的所有兄弟節(jié)點(diǎn)self.當(dāng)前節(jié)點(diǎn)自身4.3 軸使用示例# 選取當(dāng)前節(jié)點(diǎn)的所有子元素 child::book # 選取當(dāng)前節(jié)點(diǎn)的所有屬性 attribute::lang # 等價于 lang # 選取當(dāng)前節(jié)點(diǎn)所有后代中的 title descendant::title # 等價于 .//title # 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) parent::node() # 等價于 .. # 選取當(dāng)前節(jié)點(diǎn)之后的所有兄弟 book following-sibling::book # 選取當(dāng)前節(jié)點(diǎn)之前的所有兄弟 preceding-sibling::*五、XPath 謂詞Predicates謂詞用于過濾節(jié)點(diǎn)集寫在方括號[]中返回布爾值。5.1 數(shù)值謂詞# 第一個子元素 /bookstore/book[1] # 最后一個子元素 /bookstore/book[last()] # 倒數(shù)第二個 /bookstore/book[last()-1] # 前三個 /bookstore/book[position() 3]5.2 屬性謂詞# 存在 lang 屬性 //title[lang] # lang 屬性等于 en //title[langen] # 多屬性條件 //input[typetext and nameusername] # 屬性包含某值 //div[contains(class, header)] # 屬性以某值開頭 //a[starts-with(href, https)]5.3 文本謂詞# 文本完全匹配 //a[text()登錄] # 文本包含 //button[contains(text(), 提交)] # normalize-space 去除空白后匹配 //div[normalize-space(text())Hello World]5.4 邏輯運(yùn)算符# and //book[categorycooking and price25] # or //book[price10 or price50] # not() //div[not(class)] # ! //title[lang!en]六、XPath 內(nèi)置函數(shù)XPath 1.0 提供了豐富的內(nèi)置函數(shù)分為四大類。6.1 節(jié)點(diǎn)集函數(shù)函數(shù)說明last()當(dāng)前節(jié)點(diǎn)集中最后一個節(jié)點(diǎn)的位置position()當(dāng)前節(jié)點(diǎn)在節(jié)點(diǎn)集中的位置從1開始count(node-set)返回節(jié)點(diǎn)集中的節(jié)點(diǎn)數(shù)量name(node-set?)返回節(jié)點(diǎn)的名稱local-name(node-set?)返回不帶命名空間前綴的名稱namespace-uri(node-set?)返回命名空間 URI6.2 字符串函數(shù)函數(shù)說明string(obj?)將對象轉(zhuǎn)換為字符串concat(string, string, ...)字符串拼接contains(s1, s2)s1 是否包含 s2starts-with(s1, s2)s1 是否以 s2 開頭substring(s, start, len?)截取子串substring-before(s1, s2)s2 之前的部分substring-after(s1, s2)s2 之后的部分string-length(s?)字符串長度normalize-space(s?)去除首尾空白合并中間空白translate(s1, s2, s3)字符替換6.3 數(shù)值函數(shù)函數(shù)說明number(obj?)轉(zhuǎn)換為數(shù)字sum(node-set)節(jié)點(diǎn)集數(shù)值求和floor(num)向下取整ceiling(num)向上取整round(num)四舍五入6.4 布爾函數(shù)函數(shù)說明boolean(obj)轉(zhuǎn)換為布爾值not(condition)邏輯非true()返回 truefalse()返回 falselang(string)判斷節(jié)點(diǎn)語言七、XPath 運(yùn)算符7.1 完整運(yùn)算符列表運(yùn)算符說明示例|節(jié)點(diǎn)集合并//h1 | //h2加法price 10-減法price - 5*乘法price * 1.1div除法price div 2mod取模position() mod 2等于id1!不等于class!hidden小于price10小于等于price10大于price10大于等于price10and邏輯與a and bor邏輯或a or b注意XPath 中除法用div取模用mod不能用/和%。八、實(shí)戰(zhàn)場景HTML 元素定位8.1 Selenium 常用定位技巧fromseleniumimportwebdriverfromselenium.webdriver.common.byimportBy driverwebdriver.Chrome()# 1. 通過 id 定位driver.find_element(By.XPATH,//*[idusername])# 2. 通過 class 定位注意 class 是多值屬性driver.find_element(By.XPATH,//div[contains(class, login-form)])# 3. 通過文本定位driver.find_element(By.XPATH,//button[text()登錄])# 4. 通過父子關(guān)系定位driver.find_element(By.XPATH,//form[idlogin]//input[typesubmit])# 5. 通過兄弟節(jié)點(diǎn)定位driver.find_element(By.XPATH,//label[text()密碼]/following-sibling::input)# 6. 通過索引定位從1開始driver.find_element(By.XPATH,(//table//tr)[2]/td[3])8.2 Python lxml 解析示例fromlxmlimportetree html html body ul classmenu lia href9/home首頁/a/li lia href/about關(guān)于/a/li lia href/contact聯(lián)系/a/li /ul /body /html treeetree.HTML(html)# 獲取所有鏈接文本textstree.xpath(//ul[classmenu]//a/text())print(texts)# [首頁, 關(guān)于, 聯(lián)系]# 獲取所有 href 屬性hrefstree.xpath(//ul[classmenu]//a/href)print(hrefs)# [/home, /about, /contact]# 同時獲取文本和屬性itemstree.xpath(//ul[classmenu]//a)foriteminitems:print(item.text,item.get(href))九、高級技巧與注意事項9.1 XPath 軸的組合使用# 找到當(dāng)前 td 所在行的第一個 td ./ancestor::tr/td[1] # 找到當(dāng)前元素前面最近的一個 h2 ./preceding::h2[1] # 找到當(dāng)前 div 的下一個兄弟 section ./following-sibling::section[1]9.2 常見陷阱索引從 1 開始XPath 的位置索引從 1 開始不是 0class 屬性是多值的classbtn只能匹配完全等于 btn 的元素推薦用contains(class, btn)text() 只取直接子文本如果元素內(nèi)有嵌套標(biāo)簽text()只取直接文本節(jié)點(diǎn)用.或string()獲取全部文本命名空間問題有命名空間的文檔需要注冊命名空間才能正確查詢謂詞中的 and/orXPath 用and/or不是/||9.3 性能優(yōu)化建議優(yōu)先使用//會掃描整個文檔盡量縮小范圍如//div[idmain]//a避免使用//*全文檔掃描屬性定位比文本定位更穩(wěn)定復(fù)雜頁面優(yōu)先使用 id 或唯一 class 作為錨點(diǎn)十、總結(jié)XPath 是一門強(qiáng)大而簡潔的查詢語言掌握它的核心在于理解節(jié)點(diǎn)模型把 XML/HTML 看作節(jié)點(diǎn)樹熟練路徑表達(dá)式/、//、.、..、是基礎(chǔ)善用軸定位軸是 XPath 區(qū)別于 CSS 選擇器的核心優(yōu)勢靈活運(yùn)用謂詞謂詞讓定位更精確掌握常用函數(shù)contains、text()、position()等是高頻使用無論是爬蟲、自動化測試還是數(shù)據(jù)解析XPath 都是不可或缺的工具。希望本文能幫助你系統(tǒng)掌握 XPath 語法。如果本文對你有幫助歡迎點(diǎn)贊、收藏、關(guān)注三連支持