1

我正在寻找Nokogiri:has中的伪类。它应该像 jQuery 的selector一样工作。has

例如:

<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


  1. Nokogiri 中是否有关于 CSS 选择器的文档?
  2. 也许有人可以编写自定义:has伪类?这是一个如何编写选择器的示例:regexp
  3. 我可以选择使用 XPath。如何编写 XPath li:has(span.string:not(:empty))>h1>a
4

4 回答 4

5

Nokogiri 当前实现的问题: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
于 2012-08-01T17:36:48.187 回答
1

Nokogiri 没有:has选择器,这里是关于它的作用的文档 http: //ruby.bastardsbook.com/chapters/html-parsing/#h-2-2

于 2012-08-01T13:39:18.757 回答
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?因为已经声明过了。在 Nokogiri 存储库中有选择器的测试:has,但它们不起作用。我向作者报告了这个问题

于 2012-08-01T13:59:52.913 回答
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>每个流派列表的所有 's。

现在,为了更好地衡量,让我们获取所有这些<a>'s 的内部文本并将它们放入一个数组中。

@genreLinks = @artistPage.css("table th > a[href='/wiki/Music_genre']").xpath("..").css("+ td a")
@genres = []
@genreLinks.each do |genreLink|
  @genres.push(genreLink.text)
end
于 2013-10-14T18:41:17.690 回答