精品欧美一区二区三区在线观看 _久久久久国色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视频免费在线看| 男人的天堂免费| 女人高潮被爽到呻吟在线观看| 久久久久久久久伊人| 成人午夜激情免费视频| 国产精品suv一区二区三区| 国产亚洲一区二区三区不卡| 欧美一区二区三区四区视频| 国产免费黄色av| 免费观看成人高潮| 91麻豆成人久久精品二区三区| 国产精品视频免费在线观看| 亚洲欧美中文日韩v在线观看| 国产在线一区二区三区播放| 91麻豆精品在线| 欧美理论在线| 在线播放国产一区中文字幕剧情欧美| 国产人妖在线观看| 欧美天堂一区| 色婷婷激情一区二区三区| 久久久久久久久影视| 国产女人在线视频| 99久久99久久精品免费看蜜桃 | 免费看一级黄色| 国产精品x8x8一区二区| 欧美日韩不卡一区| 久久久999视频| 日本中文字幕中出在线| 中文字幕一区av| 欧美一区二区三区在线播放 | 超碰地址久久| 56国语精品自产拍在线观看| 美女网站免费观看视频| 精品人人视频| 亚洲国产乱码最新视频| 免费国产成人看片在线| 日韩在线观看www| 91看片淫黄大片一级| 国产日韩欧美一区二区| 亚洲爱情岛论坛永久| 黑人精品欧美一区二区蜜桃 | 一级日韩一级欧美| 免费观看在线色综合| 国产成+人+综合+亚洲欧洲| 久草国产精品视频| 99热在线精品观看| 97国产精品久久| 国产精品成人aaaa在线| 欧美日韩免费| 午夜免费日韩视频| 成年人免费看毛片| 亚洲欧美日韩精品一区二区| 久久先锋影音av| 精品视频在线看| 青青在线免费观看视频| xx欧美xxx| 色94色欧美sute亚洲13| 欧美一级黄色影院| 欧美在线se| 欧美一级黄色录像| 熟女人妻一区二区三区免费看| 影音先锋欧美激情| 正在播放欧美视频| 玖玖爱在线观看| 欧美人与物videos另类xxxxx| 亚洲美腿欧美激情另类| 亚洲精品乱码久久久久久久久久久久| 国产毛片一区二区三区| 中文字幕日韩精品有码视频| 手机在线中文字幕| 黄色亚洲大片免费在线观看| 欧美一区二区三区免费观看| 九九热最新视频| 国产在线观看免费一区| http;//www.99re视频| 五月天婷婷社区| 国产三级精品三级| 亚洲资源视频| 女子免费在线观看视频www| 欧美日韩中文字幕在线视频| 在线看的黄色网址| 欧美电影在线观看一区| 国产视频久久久| 婷婷国产成人精品视频| 欧美日本三区| 欧日韩不卡在线视频| 在线免费观看日韩视频| 高清国产午夜精品久久久久久| 欧美激情国产日韩| av毛片在线| 欧美视频一二三| 国产精品久久久久久9999| 久9re热视频这里只有精品| 最近2019中文字幕第三页视频| 波多野结衣不卡视频| 老鸭窝91久久精品色噜噜导演| 国产欧美日韩免费| 少妇高潮一区二区三区69| 欧美极品美女视频| 亚洲天堂av在线播放| 国产精品区一区二区三含羞草| 三级视频在线看| 国产精品色在线| 国产二区视频在线| 亚洲日日夜夜| 亚洲欧美日韩网| 久久久精品国产sm调教网站| 日韩不卡在线观看日韩不卡视频| 国产精品免费一区二区三区四区| 91高清在线视频| 精品欧美aⅴ在线网站| 亚洲天堂av一区二区三区| 欧洲乱码伦视频免费| 久久久亚洲天堂| 精品乱子伦一区二区| 欧美高清在线精品一区| 精品欧美一区免费观看α√| 日韩精品一区二区三区中文在线| 国产香蕉97碰碰久久人人| 精品国产乱码一区二区| 国产经典欧美精品| 91手机视频在线| 国产一区二区三区影视| 亚洲精品中文字幕有码专区| 日韩乱码在线观看| 国产黄色成人av| 日本免费黄色小视频| 日韩一区二区三免费高清在线观看| 亚洲视频国产视频| 欧美激情黑白配| 99麻豆久久久国产精品免费| 欧美大黑帍在线播放| 一区二区三区在线资源| 久久久精品日本| 国产喷水吹潮视频www| 国产精品久久久久影院| 日本人视频jizz页码69| 精品国产乱码久久久| 国产国产精品人在线视| 三级做a全过程在线观看| 亚洲制服丝袜av| 韩国黄色一级片| 亚洲电影成人| 精品不卡一区二区三区| 男人av在线播放| 日韩理论片久久| 丁香社区五月天| 欧美韩国日本不卡| 日本免费色视频| 天天操夜夜操国产精品| 亚洲japanese制服美女| 成年免费在线观看| 草草视频在线| 日韩一区二区三区在线观看| 亚洲国产精品免费在线观看| 国产电影精品久久禁18| 男人天堂手机在线视频| 精品午夜电影| 日产精品久久久一区二区福利 | 欧美精品一区二区三区一线天视频 | 粉嫩虎白女毛片人体| 国产亚洲一区二区三区啪| 国产视频观看一区| 中文字幕在线三区| 亚洲级视频在线观看免费1级| 好吊操这里只有精品| 久久一区二区三区四区| 色综合色综合色综合色综合| 国产精品久久久乱弄| 懂色一区二区三区av片| 天堂√8在线中文| 色综久久综合桃花网| 精品人妻少妇AV无码专区| 天天操天天色综合| 少妇愉情理伦三级| 丁香婷婷深情五月亚洲| 成年人观看网站| 午夜国产一区二区| 国产精品18毛片一区二区| 成人免费无遮挡| 日韩有码在线电影| 色呦呦视频在线| 欧美丝袜丝交足nylons图片| 久久久久久久久久久久久久久久久| 成人动漫av在线| 一道本视频在线观看| 欧美区亚洲区| 翔田千里亚洲一二三区| 超碰精品在线观看| 国产精品丝袜视频| 91破解版在线观看| 深夜福利日韩在线看| 日韩一区二区三区在线观看视频| 欧美艳星brazzers| 五月天婷婷网站| 亚洲色图视频免费播放| 不卡一区二区在线观看| 国产伦精品一区二区三区免费| 国产乱子夫妻xx黑人xyx真爽| 国产精品精品| 久久一区二区三区av| 高清一区二区三区av| 欧美一级高清免费| 人人澡人人添人人爽一区二区| 少妇精品导航| 中文在线不卡视频| 天堂视频中文在线| 日韩一区二区不卡| 中文永久免费观看| 欧美午夜精品伦理| 国产污视频在线观看| 亚洲视频小说图片| 久久精品三级视频| 久久综合久久久久88| 精品伦一区二区三区| 激情综合一区二区三区| 妓院一钑片免看黄大片| 国产一区二区你懂的| 久久成人福利视频| 欧美日韩影院| 日韩视频一二三| 一级欧洲+日本+国产| 亚洲精品一区二区三区av| 你懂的视频欧美| 久久精品国产综合精品| 一区视频网站| 91在线免费看片| 国产亚洲高清一区| 成人免费高清完整版在线观看| yiren22亚洲综合| 国产精品成人观看视频国产奇米| 蜜桃视频动漫在线播放| 国模吧一区二区三区| 美女精品导航| 久久久欧美一区二区| 日本h片在线观看| 久久久噜久噜久久综合| 国产丝袜视频在线播放| 九九热视频这里只有精品| 午夜伦理在线视频| 色综合视频网站| 国产99re66在线视频| 欧美激情综合色综合啪啪五月| 午夜在线激情影院| 欧美激情一区二区三区久久久| 日本不卡影院| 97国产一区二区精品久久呦| 黄毛片在线观看| 人体精品一二三区| 香蕉成人影院| 国产精品一区二区性色av| 成人全视频在线观看在线播放高清 | 欧美一区二区在线不卡| 国产v片在线观看| 日韩欧美国产三级电影视频| 丰满熟女一区二区三区| 亚洲变态欧美另类捆绑| 午夜视频在线播放| 亚洲欧美国产日韩天堂区| 精品视频二区| 久久亚洲电影天堂| 免费毛片在线看片免费丝瓜视频| 国内精品久久久久伊人av| 日本一区二区三区四区在线观看| 亚洲一区二区三区在线免费| 国产精品日韩一区二区免费视频| 欧美网色网址| 日韩欧美亚洲在线| 伊人情人综合网| 国产精品裸体瑜伽视频| 久久久蜜桃一区二区人| 91女神在线观看| 成人妖精视频yjsp地址| av网站有哪些| 国产精品国产精品国产专区不蜜 | 亚洲一区二区三区| 大陆av在线播放| 日日摸夜夜添夜夜添国产精品| 国产超碰在线播放| 粉嫩aⅴ一区二区三区四区 | 欧美日韩xxxx| 成人在线观看www| 国产亚洲毛片| www.com久久久| 99精品欧美一区二区三区小说| 欧洲美熟女乱又伦| 亚洲高清不卡在线| 欧美日韩在线视频播放| 日韩精品一区二区三区在线观看| 丝袜视频国产在线播放| 美女少妇精品视频| 午夜欧美巨大性欧美巨大 | 一区二区欧美国产| 黄色片视频免费| 日韩亚洲欧美综合| 韩国中文免费在线视频| 欧美激情一区二区三区成人| 成人在线黄色| 国模精品娜娜一二三区| 婷婷亚洲五月色综合| 免费无码国产v片在线观看| 精品无人区卡一卡二卡三乱码免费卡| 亚洲婷婷在线观看| 亚洲视频网在线直播| 丁香社区五月天| 亚洲第一网中文字幕| 黄视频在线观看网站| 欧美最顶级的aⅴ艳星| 成人激情自拍| 女女同性女同一区二区三区按摩| 日日夜夜精品免费视频| 星空大象在线观看免费播放| 日韩理论片一区二区| 自拍偷拍校园春色| 日韩成人中文字幕在线观看| 亚洲wwwww| 91欧美激情另类亚洲| 日韩激情图片| 精品视频一区二区在线| 99精品国产91久久久久久| 草视频在线观看| 宅男噜噜噜66一区二区66| free性中国hd国语露脸| 国产精品成人一区二区不卡| 男人日女人bb视频| 波多野结衣视频一区| 久久综合成人网| 欧美大片日本大片免费观看| 黄色网页在线免费看| 成人亲热视频网站| 久久中文亚洲字幕| 亚洲综合欧美在线| 中日韩免费视频中文字幕| 天天综合久久综合| 亚洲视频axxx| 国产精品毛片久久久久久久久久99999999 | 一级黄色在线观看| 亚洲日本中文字幕免费在线不卡| 小h片在线观看| 九色一区二区| 麻豆精品网站| 久久久久久国产免费a片| 在线一区二区三区四区五区| 黄色av免费在线观看| 国产精品27p| 欧美日韩国产高清电影| 国产一级做a爰片久久| 国产精品丝袜一区| 91在线视频国产| 久色乳综合思思在线视频| 国产精品国产三级在线观看| 欧美精品久久96人妻无码| 国产一区二区不卡老阿姨| 欧美三根一起进三p| 亚洲国产成人久久综合| 欧美日韩国产观看视频| 免费久久久一本精品久久区| 久久精品道一区二区三区| 国产精品天天干| 欧美理论片在线| 狂野欧美性猛交xxxxx视频| 精品1区2区| 日韩中文欧美在线| www色aa色aawww| 亚洲成人999| 日本综合字幕| 在线视频精品一区| 国产成人日日夜夜| 婷婷激情五月网| www.日韩欧美| 丁香婷婷成人| 欧美日韩亚洲一二三| 综合激情成人伊人| 动漫av一区二区三区| 日本精品久久久久影院| 国产精品久久久久久麻豆一区软件| 久久精品国产99久久99久久久| 亚洲国产日韩av| 国产黄色片在线观看| 亚洲综合中文字幕在线| 亚洲一区二区动漫| 最新av电影网站| 国产视频自拍一区| 国产一区二区在线观| www.中文字幕在线| 亚洲三级在线免费观看| 五月婷婷激情在线| 成人国产亚洲精品a区天堂华泰| 精品成人国产| 日本猛少妇色xxxxx免费网站| 日韩欧美一级二级| 亚州一区二区三区| 欧美激情亚洲天堂| 国产精品久久福利| 日韩av资源| 亚洲综合中文字幕在线| 日本亚洲免费观看| 亚洲 欧美 视频| 欧美做受69|