使用Nokogiri解析HTML-Ruby/Rails方式



我正在制作一个小型Rails应用程序,用于解析本地公共广播电台的HTML播放列表,并显示当前播放的歌曲。

我创建了一个类来对播放列表中的歌曲进行建模,如下所示:

require 'open-uri'
class Song
  attr_accessor :artist, :title, :album, :playtime
  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end
  def self.latest(how_many)
    html = Nokogiri::HTML(open(Rails.configuration.on_air_url))
    rows = html.css('#table_tracklist tbody tr')
    rows.take(how_many).map do |row|
      parse_song(row)
    end
  end
  private
  def self.parse_song(row)
    artist = row.css('.artist').text
    playtime = row.css('.time span').text
    title = row.css('.song').text
    album = row.css('.album').text
    Song.new({ artist: artist, playtime: playtime, title: title, album: album })
  end
end

我对此有几个问题:

  1. 我没有使用任何ActiveRecord或ActiveModel功能。这仍然属于我的models目录中的一个类吗?还是我应该将它重构为lib中的类?我计划有一个控制器,它的唯一目的是将通过JSON播放的最新歌曲返回到客户端。有更好的方法吗
  2. 我对Song::latest方法很满意,但我觉得应该有一种更优雅的方法来做Song::parse_song。我正在考虑更改我的模型的属性,以匹配播放列表使用的CSS类的名称,并使用我想要获取的属性名称数组,但由于存在"时间"字段的特殊情况(我想要获取跨度的文本),这样似乎会更清楚。你能提出一些建议吗

我认为删除我的initialize方法并这样做会更好。想法?[注:包含锡人的答案如下。]

  def self.parse_song(row)
    song = Song.new
    song.artist = row.at_css('.artist').text
    song.playtime = row.at_css('.time span').text
    song.title = row.at_css('.song').text
    song.album = row.at_css('.album').text
    song
  end

您不了解css的作用:

artist = row.css('.artist').text
playtime = row.css('.time span').text
title = row.css('.song').text
album = row.css('.album').text

应该是:

artist = row.at('.artist').text
playtime = row.at('.time span').text
title = row.at('.song').text
album = row.at('.album').text

csssearchxpath一样返回NodeSet。NodeSet就像一个节点数组。即使您知道文档中只有一个匹配元素,css仍然会返回一个集合。如果某个特定选择器有多个命中,您将收到所有匹配的节点。

当你在NodeSet上使用text时,你会得到节点中所有文本的串联字符串,这很可能不是你想要的:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
  <body>
    <p>foo</p>
    <p>bar</p>
  </body>
</html>
EOT
doc.css('p').text # => "foobar"

此外,当涉及到我们用来与之交谈的代码时,Nokogiri非常宽容/理解。我们不必使用cssxpath,我们可以使用search,让Nokogili判断选择器是CSS还是XPath:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
  <body>
    <p>foo</p>
    <p>bar</p>
  </body>
</html>
EOT
doc.css('p').size # => 2
doc.search('p').size # => 2

atat_cssat_xpath也是如此:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
  <body>
    <p>foo</p>
    <p>bar</p>
  </body>
</html>
EOT
doc.at_css('p').text # => "foo"
doc.at('p').text # => "foo"

我建议你不要懒惰,在编写代码搜索节点的99.9%的时间里使用searchat,然后在那些不得不向Nokogiri提示选择器是什么的时候使用CSS/XPath变体

相关内容

  • 没有找到相关文章

最新更新