【问题标题】:Selecting a specific table cell using CSS使用 CSS 选择特定的表格单元格
【发布时间】:2015-11-03 13:55:52
【问题描述】:

我从atpworldtour.com 中抓取了排名表,并尝试访问玩家姓名。

表格中的一行示例如下所示:

<tr>
  <td class="rank-cell">1</td>
  <td class="move-cell">
    <div class="move-none"></div>
    <div class="move-text">
    </div>
  </td>
  <td class="country-cell">
    <div class="country-inner">
      <div class="country-item">
        <img src="/~/media/images/flags/srb.png" alt="SRB" onerror="this.remove()">
      </div>
    </div>
  </td>
  <td class="player-cell">
    <a href="/en/players/novak-djokovic/d643/overview" data-ga-label="Novak Djokovic">Novak Djokovic</a>
  </td>
  <td class="age-cell">28</td>
  <td class="points-cell">
    <a href="/en/players/novak-djokovic/d643/rankings-breakdown?team=singles" data-ga-label="rankings-breakdown">15,785</a>
  </td>
  <td class="tourn-cell">
    <a href="/en/players/novak-djokovic/d643/player-activity?matchType=singles" data-ga-label="player-activity">17</a>
  </td>
  <td class="pts-cell">1,500</td>
  <td class="next-cell">0</td>
</tr>

我尝试了几种不同的方法来提取这些信息。到目前为止,我迄今为止取得的最大成功是:

url = "http://www.atpworldtour.com/en/rankings/singles"
doc = Nokogiri::HTML(open(url))

doc.css("tr").each do |row|
  puts row.css("td a")
end

问题是,每一行中玩家名字后面还有两个链接,所以我把它们放在一起。玩家的名字是表格中的第四个单元格,所以我尝试先拉出第四个单元格,然后访问链接:

doc.css("tr").each do |row|
  cell = row.css("td")[3]
  puts cell.css("a").text
end

但这会返回错误undefined method 'css' for nil:NilClass

经过进一步调查,cell 似乎存储了所有带有玩家姓名的单元格,而不仅仅是当前迭代 row 的单元格,但是当我尝试遍历 cell 时,我得到了相同的结果undefined method 错误。

我也尝试使用 XPath 解决这个问题:

doc.xpath("//tr").each do |row|
  puts row.xpath("/td[3]/a").text
end

但输出是一大片空白区域,其中应列出名称。

  1. 关于我做错了什么有什么提示吗?
  2. 谁能告诉我有关在 Nokogiri 中使用 CSS/XPath 选择器的详细文档,我将不胜感激。

到目前为止,我发现的所有内容都只涵盖了最基本的内容,而且我很难找到有关如何执行更复杂操作的信息。


我实际上是使用:

doc.xpath("//tr").each do |row|
  puts row.at_css("a").text
end

但是任何帮助找到适当的文档/教程以在 Nokogiri 中使用 XPath 和 CSS 选择器仍然很棒。

【问题讨论】:

  • doc.css('.player-cell a').map(&amp;:text) 呢?
  • 是的,这也很有效,而且更好,谢谢。如果您可以将其与解释一起放在答案中,我将很高兴接受它:)
  • 向我们询问教程链接的问题是题外话。这是您研究答案的一部分。 Stack Overflow 中包含显示和使用 CSS/XPath 和 Nokogiri 的完整答案,因此请在此处搜索并阅读 Nokogiri 节点文档。复杂搜索是使用简单搜索构建的;复杂的选择器掩盖了问题,所以从小的搜索开始,慢慢地聚合和测试,看看你是否仍然可以达到你需要的东西。

标签: ruby nokogiri


【解决方案1】:

也许这将有助于了解正在发生的事情:

require 'nokogiri'
doc = Nokogiri::HTML('<table><tr><td>foo</td><td>bar</td></tr></table>')

at 返回第一个匹配的节点。在这种情况下,它是&lt;tr&gt;。使用text 会返回其中的所有文本连接在一起:

doc.at('tr').to_html # => "<tr>\n<td>foo</td>\n<td>bar</td>\n</tr>"
doc.at('tr').text # => "foobar"

使用search 返回一个NodeSet,它最容易被认为是一个数组。在这种情况下,它将返回两个元素,每个 &lt;tr&gt;&lt;td&gt; 对一个:

doc.search('tr td').size # => 2

text 将返回 NodeSet 中所有节点的文本,再次连接字符串:

doc.search('tr td').to_html # => "<td>foo</td>\n<td>bar</td>"
doc.search('tr td').text # => "foobar"

但是,通过遍历 NodeSet 中的每个节点,我们可以看到单独的文本:

doc.search('tr td').map(&:text) # => ["foo", "bar"]

另一种但稍慢的方法是先找到&lt;tr&gt; 节点,然后在其中搜索单个&lt;td&gt; 节点:

doc.at('tr').search('td').size # => 2
doc.at('tr').search('td').to_html # => "<td>foo</td>\n<td>bar</td>"
doc.at('tr').search('td').text # => "foobar"

同样,使用map,我们可以遍历它们并获得没有连接的文本:

doc.at('tr').search('td').map(&:text) # => ["foo", "bar"]

这是使用单个选择器与单独选择器下降并选择 &lt;td&gt; 节点的速度差异:

require 'fruity'
require 'nokogiri'

doc = Nokogiri::HTML('<table><tr><td>foo</td><td>bar</td></tr></table>')

compare do
  single_selector { doc.search('tr td').map(&:text) }
  separate_selectors { doc.at('tr').search('td').map(&:text) }
end
# >> Running each test 32 times. Test will take about 1 second.
# >> single_selector is faster than separate_selectors by 2x ± 0.1

不同之处在于 tr td 对 libXML2 的一次往返调用,而doc.at('tr').search('td') 则两次调用。

不幸的是,有时如果我们需要使用条件逻辑或按照它们在标记中出现的顺序访问多个不同类型的子节点,我们不得不使用更长、更慢的形式.

【讨论】:

    【解决方案2】:

    包含玩家姓名的表格单元格有一个类player-cell

    <td class="player-cell">
      <a href="/en/players/novak-djokovic/d643/overview" data-ga-label="Novak Djokovic">Novak Djokovic</a>
    </td>
    

    你可以使用这个类来获取元素:

    doc.css('.player-cell a').map(&:text)
    #=> ["Novak Djokovic", "Roger Federer", "Andy Murray", ...]
    

    即使没有明确的类,您也可以通过以下方式获取第 4 列:

    doc.css('td:nth-child(4) a').map(&:text)
    #=> ["Novak Djokovic", "Roger Federer", "Andy Murray", ...]
    

    或者使用 XPath:

    doc.xpath('//td[4]/a').map(&:text)
    #=> ["Novak Djokovic", "Roger Federer", "Andy Murray", ...]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-14
      • 2016-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-20
      • 2013-05-02
      相关资源
      最近更新 更多