|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
如果你只是想应付一下操作系统的课程,劝你最好别学,或者说不要指望能用的怎么样。
我第一次打仗XPath是在2007年,但比来才入手下手对它发生乐趣。之前在年夜多半情形下我城市只管制止利用它,而当我不能不实验利用它时,每次都以失利了结。当时XPath对我来讲并没有甚么意义。
可是厥后我碰到了一个特别的剖析成绩(对CSS选择器来讲过于庞大,而用手工代码的话又过于复杂),因而我决意再实验一次XPath。令我感应欣喜的是,这切实其实行得通,并且很有效。
以下是我的亲自履历
我碰到的成绩
假定你办理一个歌词网站,为了保持分歧的浏览体验,你要搜集每行歌词的第一个单词。假如歌词利用纯文本格局保留,那末能够间接用上面的代码来完成。
1
lyrics.gsub!(/^./){|character|character.upcase}
可是假如歌词被保留肯html格局就没有这么复杂了,由于dom布局自己并没有”行”的观点,以是没有举措利用一个复杂的正则表达式来辨认行。
以是我们要做的第一件事变是界说甚么是dom布局中的“行的出发点”,上面是两个复杂的例子:
- <p>标签中第一个文本节点
- <br>前面的第一个文本节点
就像上面如许:
1
<p>Thisisthebeginningofaline.Thisistoo.</p>
可是除此以外我们大概还要处置嵌套的行内元素:
1
<p><em>This</em>isthebeginningofaline.<strong>Thisisnot.</strong></p>
惯例的办理计划
我想到的第一个办理办法是用Ruby写一个办法来扫描dom中一切相干的部分并递回找出一切切合前提的节点。个中用到了几个轻量级的css选择器:
1
2
3
4
5
6
7
8
9
10
11
defeach_new_line(document)
document.css(p).each{|p|yieldfirst_text_node(p)}
document.css(br).each{|br|yieldfirst_text_node(br.next)}
end
deffirst_text_node(node)
ifnode.nil?thennil
elsifnode.text?thennode
elsifnode.children.any?thenfirst_text_node(node.children.first)
end
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表达式是由一个或多个被/支解的定位步(locationsteps)构成。表达式中的第一个/代表了文档(document)的根节点。每一个定位步都标明了已被婚配的节点并转达一下三条信息:
我想从以后的地位挪动到哪?
谜底是轴(Axis),是可选的。默许的轴是child,暗示“以后被选中节点的一切子节点”。在下面的例子中,descendant-or-self是第一个定位部的轴,暗示“一切以后被选中的节点和他们一切的子节点”。年夜部分XPath标准中界说的轴都有像“descendant-or-self”如许的语义化的名字。
我想要选择甚么范例的节点?
选择的内容是由节点测试来指定的,这也是每一个定位步中不成短少的部分。在我们之前的例子中,node()婚配的是全体范例;text()婚配到的是文本节点;element()只能婚配到元素,并必需指明节点称号(像p,em等),节点称号必填。
大概增添分外的过滤器吗?
大概我们只想选择以后一切节点的第一个子元素或只想选则有href属性的<a>标签。关于此类断言(assertion),我们可使用谓词(predicates)依据分外的遍历树(additionaltreetraversals)来过滤出切合前提的节点。如许我们就能够依据这些节点的属性(children,parents,orsiblings)来过滤出切合前提的节点。
我们的例子中没有谓词,如今让我们来加一个只婚配有href属性的<a>标签:
1
/descendant-or-self::node()/child::p/child::em/child::a[attribute::href]
固然谓词看上往很像一个括号中的定位步,可是谓词中的“节点测试(nodetest)”部分有比定位步中的节点测试更多的功效。
换一个角度来看XPath
与一个加强型的CSS选择器比拟,XPath与JQuery的便当更类似。比方,我们能够把之前的XPath表达式换成JQuery的情势:
1
2
3
4
$(document).find(*).
children(p).
children(em).
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是同意如许做的。到场换行的辨认后,代码以下:
<p>1
2
3
4
/descendant-or-self::node()/
child::br/
following-sibling::node()[position=1]/
<p> |
|