精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

遍歷dom的工具 XPath真不簡單

開發 前端
我第一次接觸XPath是在2007年,但最近才開始對它產生興趣。以前在大多數情況下我都會盡量避免使用它,而當我不得不嘗試使用它時,每次都以失敗告終。那時XPath對我來說并沒有什么意義。

我第一次接觸XPath是在2007年,但最近才開始對它產生興趣。以前在大多數情況下我都會盡量避免使用它,而當我不得不嘗試使用它時,每次都以失敗告終。那時XPath對我來說并沒有什么意義。

但是后來我遇到了一個特殊的解析問題(對CSS選擇器來說過于復雜,而用手工代碼的話又過于簡單),于是我決定再嘗試一次XPath。令我感到驚喜的是,這的確行得通,而且很有用。

以下是我的親身經歷

我遇到的問題

假設你管理一個歌詞網站,為了維持一致的閱讀體驗,你要收集每行歌詞的第一個單詞。如果歌詞使用純文本格式保存,那么可以直接用下面的代碼來實現。

  1. lyrics.gsub!(/^./) { |character| character.upcase } 

但是如果歌詞被保存肯html格式就沒有這么簡單了,因為dom結構本身并沒有”行”的概念,所以沒有辦法使用一個簡單的正則表達式來識別行。

所以我們要做的第一件事情是定義什么是dom結構中的“行的起點”,下面是兩個簡單的例子:

  • <p>標簽中第一個文本節點
  • <br>后面的第一個文本節點

就像下面這樣:

  1. <p>This is the beginning of a line.This is too.</p> 

但是除此之外我們可能還要處理嵌套的行內元素:

  1. <p><em>This</em> is the beginning of a line. <strong>This is not.</strong></p> 

常規的解決方案

我想到的第一個解決方法是用Ruby寫一個方法來掃描dom中所有相關的部分并遞歸找出所有符合條件的節點。其中用到了幾個輕量級的css選擇器:

  1. def each_new_line(document)  
  2.   document.css('p').each { |p| yield first_text_node(p) }  
  3.   document.css('br').each { |br| yield first_text_node(br.next) }  
  4. end  
  5.    
  6. def first_text_node(node)  
  7.   if node.nil? then nil  
  8.   elsif node.text? then node  
  9.   elsif node.children.any? then first_text_node(node.children.first)  
  10.   end  
  11. end 

這是一個比較合理的解決方案,但是11行的代碼似乎有點兒長。有點兒殺雞用牛刀的感覺,僅僅為了獲得dom的節點而用上Ruby的迭代器和條件語句感覺有點兒犯不上。應該有更好的辦法吧?

終于說到正題了(XPath)

XPath有一下幾個原因容易讓人困惑。第一點是網上幾乎沒有可以參考的東西(W3Schools!就不用想了)。RFC已經是我找到的最好的文檔了。

第二點是XPath看上去有點兒像CSS。方法名里就有“path”,所以我總是假設XPath的表達式中的 / 和CSS選擇器中的 > 是一個意思。

  1. document.xpath('//p/em/a') == document.css('p > em > a')  

其實,XPath表達式包含了許多簡寫,如果我們想要弄清楚上面代碼運行時究竟發生了什么就必須要弄清楚這些簡寫。下面是用全拼寫出來的相同的表達式:

  1. /descendant-or-self::node()/child::p/child::em/child::a/  

這個XPath表達式和上面的CSS選擇器的作用是一樣的,但并不像我之前假設的那樣。一個XPath表達式是由一個或多個被 / 分割的定位步(location steps)組成。表達式中的第一個 / 代表了文檔(document)的根節點。每個定位步都表明了已經被匹配的節點并傳達一下三條信息:

我想從當前的位置移動到哪?

答案是軸(Axis),是可選的。默認的軸是child,表示“當前被選中節點的所有子節點”。在上面的例子中,descendant-or-self是第一個定位部的軸,表示“所有當前被選中的節點和他們所有的子節點”。大部分XPath規范中定義的軸都有像“descendant-or-self”這樣的語義化的名字。

我想要選擇什么類型的節點?

選擇的內容是由節點測試來指定的,這也是每個定位步中不可缺少的部分。在我們之前的例子中,node()匹配的是全部類型;text()匹配到的是文本節點;element()只能匹配到元素,并必須指明節點名稱(像p,em等),節點名稱必填。

可能增加額外的過濾器嗎?

也許我們只想選擇當前所有節點的第一個子元素或只想選則有href屬性的<a>標簽。對于此類斷言(assertion),我們可以使用謂詞(predicates)根據額外的遍歷樹(additional tree traversals)來過濾出符合條件的節點。這樣我們就可以根據這些節點的屬性(children, parents, or siblings)來過濾出符合條件的節點。

我們的例子中沒有謂詞,現在讓我們來加一個只匹配有href屬性的<a>標簽:

  1. /descendant-or-self::node()/child::p/child::em/child::a[attribute::href]  

雖然謂詞看上去很像一個括號中的定位步,但是謂詞中的“節點測試(node test)”部分有比定位步中的節點測試更多的功能。

#p#

換一個角度來看XPath

與一個增強型的CSS選擇器相比,XPath與JQuery的便利更相似。例如,我們可以把之前的XPath表達式換成JQuery的形式:

  1. $(document).find('*').  
  2.   children('p').  
  3.   children('em').  
  4.   children('a').filter('[href]')  

上面的代碼中,我們用到的JQuery的方法與軸的作用是一樣的:

  1. .children()相當于軸中的child,.find()相當于descendant。 

JQuery方法中的選擇器相當于XPath中的節點測試,只可惜jQuery不允許選擇文本節點。

jQuery中的.filter()方法相當于XPath中的謂詞,.children(‘em’)的作用是匹配所有匹配到的<p>標簽中的所有<em>子元素。這樣看來,XPah要比jQuery強大得多。

讓我們回到識別行首的問題

現在我們對XPath的工作原理已經有了深入的了解,下面來用它解決之前提到的問題。首先我們先把問題簡化一下,只尋找每段的第一個文本節點:

  1. /descendant-or-self::node()/child::p/child::text()[position()=1]  

上面的代碼的作用依次是:

  • 1.尋找文檔中的所有節點
  • 2.尋找這些節點的所有為<p>的子節點
  • 3.尋找這些<p>的文本子節點
  • 4.只保留這些節點中符合條件的第一個元素

注意position() function 在代碼中表示的是每個<p>中的第一個文本子節點而不是整個文檔中的第一個<p>的文本子節點。

接下來,為了找到<p>中被嵌套得很深的文本節點,我們把child換成descendant

  1. /descendant-or-self::node()/child::p/descendant::text()[position()=1]  

接下來是識別換行的問題,首先我們給這一長串代碼折下行(因為太長了),XPath是允許這樣做的。加入換行的識別后,代碼如下:

  1. /descendant-or-self::node()/  
  2. child::br/  
  3. following-sibling::node()[position=1]/  
  4. descendant-or-self::text()[position()=1]  

每一行代碼的意思分別是:

  • 1.找到所有節點
  • 2.找到到這些節點的<br>子節點
  • 3.找到這些<br>的下一個同級節點
  • 4.如果上面取到的不是文本節點,則取它們的子節點中的第一個文本節點

這樣一來我們就可以同時選出<p>中和<br>后的新的一行。下面我們以上的代碼合并成一個表達式:

  1. (/descendant-or-self::node()/child::p|  
  2. /descendant-or-self::node()/child::br/following-sibling::node()[position=1])/  
  3. descendant-or-self::text()[position()=1]  

最后我們把簡寫替換進去:

  1. (//p|//br/following-sibling::node()[position=1])/  
  2.  descendant-or-self::text()[position=1]  

這樣我們就把一個復雜的概念用一個簡單的表達式表示出來了。如果我們想加入更多的對行的操作,只需要往實現匹配的代碼中加入更多的元素名稱就可以了。

我們究竟能從中獲得什么?

既然我們能用相對易懂的Ruby來實現為什么還要選擇XPath呢?

大多數情況下,Ruby是用來編寫高水平代碼的,例如商業邏輯,整合應用組件,描述復雜的領域模型。從中可以看出最好的Ruby代碼是用來描述意圖而非用于實現。所以用Ruby來做一些低水平或與應用無關的事情(遍歷dom樹來找指定屬性的節點)讓人蛋疼。

XPath的其中一個優勢是它的速度:XPath的遍歷是通過libxml實現的,而原生代碼的速度是非??斓?。對于我上面舉的例子,與Ruby的實現相比,XPath實際上要慢得多。我猜導致這個情況的原因是對于<br>標簽的下一個元素的查找。因為在這個動作中實際上是先篩選出了<br>后面的所有與之同級的元素然后才過濾出其中的第一個。

所以XPath快慢與否取決于你的使用方式,但是上手有點兒難。這是一個專門用來讓你使用簡潔的慣用表達式來遍歷dom的工具。

原文鏈接: rapgenius   翻譯: 伯樂在線 - 楊帥

譯文鏈接: http://blog.jobbole.com/58160/

責任編輯:林師授 來源: 伯樂在線
相關推薦

2021-10-19 08:20:47

單例模式設計模式面試

2023-05-17 07:36:00

淺拷貝深拷貝對象

2017-12-25 15:35:36

iMac Pro芯片存儲

2020-12-16 07:36:46

Redis字符串數據

2019-02-21 10:06:49

2014-12-19 10:07:10

C

2021-01-11 07:51:16

DOM對象節點樹

2022-03-16 22:24:50

ReactstateHooks

2012-06-26 09:40:14

部署開發管理

2009-07-20 10:06:47

虛擬化思杰操作系統

2010-01-19 10:10:28

2010-12-06 09:45:27

TechEd 2010

2011-10-26 11:06:01

IBM朱近之華為

2020-11-11 15:36:51

服務器

2010-03-30 14:06:35

2014-08-21 10:14:09

APP界面設計移動客戶端

2023-10-30 10:11:09

2013-06-04 17:10:00

Linux命令

2011-12-28 15:11:09

iOS推薦

2010-09-13 14:24:17

JavaScript
點贊
收藏

51CTO技術棧公眾號

久久久久久一二三区| 免费毛片在线不卡| 一卡二卡三卡日韩欧美| 国产精品区二区三区日本| 日韩污视频在线观看| 国产一区二区三区不卡视频网站| 日韩欧美视频一区二区三区| 神马影院我不卡| 97在线播放免费观看| 国精品一区二区| 久久久亚洲欧洲日产国码αv| 日韩av成人在线| 青青操在线播放| 亚洲视频一起| 一本久道久久综合中文字幕| 曰韩不卡视频| 手机看片一区二区| 蜜乳av一区二区三区| 亚洲精品国产精品乱码不99按摩 | 99久久婷婷国产综合精品首页| 亚洲图片激情小说| 日本韩国在线不卡| 日韩av手机在线免费观看| 国产精品三p一区二区| 欧美日韩视频第一区| 69sex久久精品国产麻豆| 国产三级在线看| 风间由美一区二区三区在线观看| 国产ts一区二区| 中文字幕乱码av| 猛男gaygay欧美视频| 日韩区在线观看| 久久久精品麻豆| 僵尸再翻生在线观看| 亚洲日本一区二区| 婷婷久久伊人| 国产成人欧美在线观看| 精品久久久久久无码国产| www在线观看播放免费视频日本| 91视视频在线观看入口直接观看www| 国产一区二区色| 精品国产乱子伦| 在线成人黄色| 欧美国产日韩免费| 97在线观看免费高| 日韩中文欧美| 在线不卡国产精品| a级片在线观看| 婷婷成人综合| 亚洲国产日韩欧美综合久久| 97人人模人人爽人人澡| 嫩草在线视频| 国产三级一区二区| 久热国产精品视频一区二区三区| 午夜免费福利视频| 国产一区免费电影| 亚洲aⅴ男人的天堂在线观看| 中文字幕乱码一区二区| 91精品精品| 中文字幕综合在线| 国产传媒在线看| 色一区二区三区四区| 伊人久久五月天| 卡一卡二卡三在线观看| 欧洲乱码伦视频免费| 亚洲最新av在线| 中文字幕人妻一区二区| 一区二区三区四区在线看| 日韩理论片久久| 精品人妻无码一区二区三区| 天堂99x99es久久精品免费| 日韩经典中文字幕| 天天干天天操天天做| 成人性片免费| 欧美日本高清视频在线观看| 三级一区二区三区| 中文字幕日韩在线| 日韩av影片在线观看| 添女人荫蒂视频| 国产一区二区精品久| 中文字幕在线国产精品| 中文字幕五月天| 激情欧美一区| 欧美一级视频在线观看| 日韩中文字幕高清| 激情丁香综合五月| aa日韩免费精品视频一| 神马久久高清| 91久色porny| 伊人久久大香线蕉精品| 日本电影在线观看| 一本色道久久综合狠狠躁的推荐| 看欧美ab黄色大片视频免费 | 一区二区三区四区五区视频在线观看 | 两个人的视频www国产精品| 久久久综合久久| 免费精品视频| 成人亚洲欧美一区二区三区| 黑人精品一区二区| 欧美国产综合色视频| 2022中文字幕| 在线视频三区| 一区二区三区在线免费| 无码aⅴ精品一区二区三区浪潮| fc2ppv国产精品久久| 亚洲国产成人av好男人在线观看| 97xxxxx| 国产精品igao视频网网址不卡日韩| 亚洲第一偷拍网| 国产视频不卡在线| 亚洲成人在线| 成人黄色免费在线观看| 污视频在线免费| 亚洲欧美福利一区二区| 成年网站在线免费观看| 日韩精品视频一区二区三区| 亚洲视频日韩精品| 久久人人爽人人爽人人| 蜜臀av性久久久久蜜臀aⅴ流畅 | 久久美女高清视频| 黄色特一级视频| 日本欧美韩国| 日韩精品免费观看| 69av视频在线| 久热成人在线视频| 国产精品吴梦梦| 视频一区二区三区在线看免费看| 夜夜亚洲天天久久| 狠狠干狠狠操视频| 国产剧情在线观看一区| 66m—66摸成人免费视频| 69精品久久久| 久久99最新地址| 亚洲精品欧美日韩| 第九色区av在线| 五月综合激情日本mⅴ| 欧美视频亚洲图片| 色一区二区三区四区| 国产精品va在线播放| 日本午夜在线视频| 欧美色图在线视频| 美女黄色一级视频| 亚洲天堂成人| 超碰在线观看97| bt在线麻豆视频| 91麻豆精品国产自产在线观看一区 | av老司机免费在线| 欧美成人一区二区| 99中文字幕在线| 欧美特黄一级大片| 欧美最近摘花xxxx摘花| 天堂8在线视频| 亚洲一区二区成人在线观看| 日本黄色www| 欧美一区二区| 2019国产精品视频| 在线中文字幕-区二区三区四区| 欧美日韩一级二级| 日韩欧美视频免费观看| 麻豆免费精品视频| 亚洲午夜久久久影院伊人| 国产福利91精品一区二区| 在线播放亚洲激情| 91麻豆成人精品国产| 中文字幕一区二区三中文字幕| www.久久久精品| 羞羞答答成人影院www| 亚洲伊人久久综合| 丁香花在线电影小说观看| 欧美性猛交xxxx| 添女人荫蒂视频| 视频一区二区三区在线| 午夜一区二区三区| www.久久99| 九九精品视频在线| 免费观看a视频| 欧美视频中文字幕在线| 中文字幕免费在线看线人动作大片| 青草av.久久免费一区| 亚洲欧美电影在线观看| 日韩成人在线看| 91成品人片a无限观看| 福利在线午夜| 91麻豆精品国产91久久久更新时间 | 成人三级av在线| 欧美在线视频导航| 在线观看国产原创自拍视频| 日韩一区二区高清| 久久久精品免费看| 中文字幕av资源一区| 僵尸世界大战2 在线播放| 欧美电影完整版在线观看| 久久久999精品免费| 性网爆门事件集合av| 疯狂做受xxxx欧美肥白少妇| 免费看黄色三级| 国产精品 欧美精品| 国产亚洲精品网站| 91麻豆精品国产91久久久平台| 91po在线观看91精品国产性色| 免费成人av电影| 欧美高清视频一二三区| 91看片在线播放| 欧美国产精品一区二区三区| 老女人性生活视频| 免费亚洲网站| 红桃一区二区三区| 欧美日韩xxxx| 97久久天天综合色天天综合色hd| 一区二区三区短视频| 久久成人一区二区| 国产尤物视频在线| 亚洲精品在线网站| 一级特黄录像免费看| 精品福利在线观看| 2021亚洲天堂| 中文字幕不卡三区| 成人免费网站黄| 国产成人av电影在线| 国产一伦一伦一伦| 国产农村妇女毛片精品久久莱园子| 中文字幕久精品免| 精品一区免费| 国产手机精品在线| 精品国产乱码一区二区三区| 国产精品7m视频| 第一福利在线视频| 欧美精品电影免费在线观看| 日韩专区在线| 一本大道亚洲视频| 四虎成人免费在线| 亚洲国产欧美自拍| 黄色av小说在线观看| 欧美电影影音先锋| 一本久道久久综合无码中文| 色999日韩国产欧美一区二区| 国产乡下妇女做爰视频| 亚洲女人****多毛耸耸8| 四虎影视一区二区| 欧美激情一区三区| 久久久久亚洲av无码a片| 久久先锋影音av| 日韩aaaaa| 成人久久18免费网站麻豆| 伦伦影院午夜理论片| 国产一区二区三区免费看| 一区二区三区入口| 免费观看成人av| 激情五月俺来也| 视频精品一区二区| 免费在线观看的毛片| 久久国产66| 久热免费在线观看| 三级久久三级久久| www.99在线| 美女性感视频久久| 亚洲小视频网站| 激情图区综合网| 色综合色综合色综合色综合| 免费欧美在线视频| 久久久精品高清| 国产一区二区视频在线播放| gogo亚洲国模私拍人体| 丁香婷婷综合网| 欧美双性人妖o0| 久久亚洲精品小早川怜子| 日本少妇高潮喷水xxxxxxx| 国产日韩视频一区二区三区| 亚洲一二三精品| 亚洲三级免费电影| 国产一级视频在线播放| 天天亚洲美女在线视频| 一级片免费在线播放| 欧美色图一区二区三区| japanese国产| 欧洲色大大久久| 在线观看色网站| 日韩无一区二区| 婷婷丁香花五月天| 国产一区二区三区视频| 免费在线观看av| 欧美精品videosex性欧美| 国产精品粉嫩| 成人做爽爽免费视频| jazzjazz国产精品麻豆| 欧美精品中文字幕一区二区| 久久免费精品视频在这里| 337p亚洲精品色噜噜狠狠p| 一区二区福利| 午夜av中文字幕| 久久综合狠狠综合久久综合88 | 国产精品热久久久久夜色精品三区 | 中文字幕中文字幕一区三区| 伊人久久婷婷| 亚洲精品一二三四五区| 日韩精品中文字幕久久臀| 国产无遮挡呻吟娇喘视频| 色视频成人在线观看免| ,亚洲人成毛片在线播放| 精品国产乱码久久久久久牛牛 | 久久久综合香蕉尹人综合网| 精品久久一区| 日韩精品久久一区二区| 日韩黄色免费电影| 91成人在线观看喷潮蘑菇| 国产三级三级三级精品8ⅰ区| 欧美激情精品久久| 日本高清不卡视频| 开心激情综合网| 日韩网站在线观看| 四虎在线视频免费观看| 中文字幕亚洲图片| 成人三级高清视频在线看| 91精品视频观看| 欧美人与拘性视交免费看| www.99riav| 蜜臀av一区二区在线观看| 中文字幕在线观看的网站| 亚洲黄一区二区三区| 中文字幕自拍偷拍| 国产视频综合在线| heyzo高清在线| 91日韩在线视频| 激情婷婷综合| 日韩免费一级视频| 成人在线一区二区三区| 视频国产一区二区| 欧美视频日韩视频在线观看| 香蕉视频网站在线| 欧美激情一二三| 精品国模一区二区三区欧美 | 噜噜噜噜噜在线视频| 国内揄拍国内精品少妇国语| 国内精品视频| 永久免费精品视频网站| 美女视频免费一区| 成年人在线免费看片| 日韩欧美国产免费播放| 午夜福利一区二区三区| 91国产精品91| 风间由美性色一区二区三区四区| 中文字幕在线乱| 久久精品99国产精品| 五月天精品在线| 欧美午夜片在线观看| 国产三级在线观看| 国产精品96久久久久久| 亚洲桃色综合影院| 日本精品一区在线观看| 91视频免费播放| 久久中文字幕免费| 亚洲欧美日韩在线高清直播| 深夜在线视频| 蜜桃麻豆www久久国产精品| 国产日韩一区二区三区在线播放| 亚洲天堂资源在线| 欧美日韩激情视频8区| 青青操视频在线| 国产精品扒开腿做爽爽爽视频| av伊人久久| 中文字幕久久av| 亚洲免费观看视频| 亚洲精品911| 国模叶桐国产精品一区| 欧美人与动xxxxz0oz| 亚洲午夜精品久久久久久人妖| 97久久人人超碰| 亚洲色图27p| 91精品婷婷国产综合久久性色| 国产1区在线| 国产精品免费一区二区三区| 亚洲美女91| 亚洲欧美日韩一级| 国产精品成人免费精品自在线观看| 国产一区二区三区三州| 久久6免费高清热精品| 果冻天美麻豆一区二区国产| 国产免费一区二区视频| 久久人人爽人人爽| 一级片在线免费观看视频| 欧美二区在线播放| 伊人久久综合影院| 特黄视频免费观看| 亚洲成人av电影| 成人性生交大片免费看午夜| 国产综合在线视频| 妖精一区二区三区精品视频| 中文字幕 91| 亚洲国产成人av好男人在线观看| 久草在现在线| 91精品中国老女人| 日韩视频久久| 国产激情av在线| 精品国产三级a在线观看| 性爽视频在线| 公共露出暴露狂另类av| 91麻豆免费看| 国产亲伦免费视频播放| 97**国产露脸精品国产| 欧美好骚综合网|