我正在寻找Nokogiri中的假类:has
。
它应该像jQuery的has
selector一样工作。
例如:
<li><h1><a href="dfd">ex1</a></h1><span class="string">sdfsdf</span></li>
<li><h1><a href="dsfsdf">ex2</a></h1><span class="string"></span></li>
<li><h1><a href="sdfd">ex3</a></h1></li>
CSS选择器应仅返回第一个链接,即非空span.string
兄弟链接。
在jQuery中,这个选择器效果很好:
$('li:has(span.string:not(:empty))>h1>a')
但不是Nokogiri:
Nokogiri::HTML(html_source).css('li:has(span.string:not(:empty))>h1>a')
:not
和:empty
效果很好,但不是:has
。
:has
伪类?以下是example如何编写:regexp
选择器。li:has(span.string:not(:empty))>h1>a
编写XPath?答案 0 :(得分:5)
problem with Nokogiri's current implementation of :has()
是它创建XPath,要求内容是直接子项,而不是任何后代:
puts Nokogiri::CSS.xpath_for( "a:has(b)" )
#=> "//a[b]"
#=> Should output "//a[.//b]" to be correct
为了使这个XPath与jQuery的匹配,你需要允许span
成为后代元素。例如:
require 'nokogiri'
d = Nokogiri.XML('<r><a/><a><b><c/></b></a></r>')
d.at_css('a:has(b)') #=> #<Nokogiri::XML::Element:0x14dd608 name="a" children=[#<Nokogiri::XML::Element:0x14dd3e0 name="b" children=[#<Nokogiri::XML::Element:0x14dd20c name="c">]>]>
d.at_css('a:has(c)') #=> nil
d.at_xpath('//a[.//c]') #=> #<Nokogiri::XML::Element:0x14dd608 name="a" children=[#<Nokogiri::XML::Element:0x14dd3e0 name="b" children=[#<Nokogiri::XML::Element:0x14dd20c name="c">]>]>
对于您的具体情况,这里是完整的“破损”XPath:
puts Nokogiri::CSS.xpath_for( "li:has(span.string:not(:empty)) > h1 > a" )
#=> //li[span[contains(concat(' ', @class, ' '), ' string ') and not(not(node()))]]/h1/a
这里修好了:
# Adding just the .//
//li[.//span[contains(concat(' ', @class, ' '), ' string ') and not(not(node()))]]/h1/a
# Simplified to assume only one CSS class is present on the span
//li[.//span[@class='string' and not(not(node()))]]/h1/a
# Assuming that `not(:empty)` really meant "Has some text in it"
//li[.//span[@class='string' and text()]]/h1/a
# ..or maybe you really wanted "Has some text anywhere underneath"
//li[.//span[@class='string' and .//text()]]/h1/a
# ..or maybe you really wanted "Has at least one element child"
//li[.//span[@class='string' and *]]/h1/a
答案 1 :(得分:1)
Nokogiri没有:has
选择器,这是做所做的文档:
http://ruby.bastardsbook.com/chapters/html-parsing/#h-2-2
答案 2 :(得分:1)
好的,我找到了一个可能对某人有用的解决方案。
自定义伪类:custom_has
:
class MyCustomSelectors
def custom_has node_set, selector
node_set.find_all { |node| node.css(selector).present? }
end
end
#usage:
doc.css('li:custom_has(span.string:not(:empty))>h1>a',MyCustomSelectors.new)
为什么我声明:custom_has
而不只是:has
?因为它已经宣布了。
对于:has
选择器,Nokogiri仓库中tests,但它们不起作用。我reported this issue给作者。
答案 3 :(得分:0)
Nokogiri允许在同一个对象上链接.css()
和.xpath()
个调用。因此,只要您想要使用:has
,只需结束当前的.css()
调用并添加.xpath(..)
(父选择器)即可。您甚至可以通过另一个.css()
来电恢复您的选择,从xpath()
停止开始!
示例:
这是来自维基百科的一些HTML:
<tr>
<th scope="row" style="text-align:left;">
Origin
</th>
<td>
<a href="/wiki/Edinburgh" title="Edinburgh">Edinburgh</a>
<a href="/wiki/Scotland" title="Scotland">Scotland</a>
</td>
</tr>
<tr>
<th scope="row" style="text-align:left;">
<a href="/wiki/Music_genre" title="Music genre">Genres</a>
</th>
<td>
<a href="/wiki/Electronica" title="Electronica">Electronica</a>
<a href="/wiki/Intelligent_dance_music" title="Intelligent dance music">IDM</a>
<a href="/wiki/Ambient_music" title="Ambient music">ambient</a>
<a href="/wiki/Downtempo" title="Downtempo">downtempo</a>
<a href="/wiki/Trip_hop" title="Trip hop">trip hop</a>
</td>
</tr>
<tr>
<th scope="row" style="text-align:left;">
<a href="/wiki/Record_label" title="Record label">Labels</a>
</th>
<td>
<a href="/wiki/Warp_(record_label)" title="Warp (record label)">Warp</a>
<a href="/wiki/Skam_Records" title="Skam Records">Skam</a>
<a href="/wiki/Music70" title="Music70">Music70</a>
</td>
</tr>
假设您要选择包含<a>
链接的<td>
之后的第一个<th>
内的所有href="/Music_genre"
元素。
@artistPage.css("table th > a[href='/wiki/Music_genre']").xpath("..").css("+ td a")
这将返回每个类型列表的所有<a>
。
现在我们要抓住所有<a>
的内部文本并将它们放在一个数组中。
@genreLinks = @artistPage.css("table th > a[href='/wiki/Music_genre']").xpath("..").css("+ td a")
@genres = []
@genreLinks.each do |genreLink|
@genres.push(genreLink.text)
end