1

我正在尝试用 Ruby 编写一个脚本来使用 Nokogiri 和 CSS 选择器来解析维基百科的文章。不过,我对脚本中的条件有点困惑。这是我到目前为止所拥有的(page是使用 Nokogiri 下载的 html):

page.css('h3').each do |node|
  puts node.text
end

page.css('li').each do |node|   
  if /\d|\D/.match(node)
    puts node.text.scan(/[\d]+\D*/).first
  end
end

page.css('td b').each do |node|
  puts node.text
end

这一切都很好。但是,我真正想要的是这样的:

page.css('h3, li, td b').each do |node|
  # if it's an h3 node, do one thing
  # if it's a li node, do another thing
  # else if it's a 'td b' node, do another thing
end

这将允许按顺序解析页面,而不是分别通过正文三次。但是,我不确定如何在我的脚本中编写这些条件。

编辑:所以现在我的脚本是

page.css('h3, li, td b').each do |node|
        case node.name
        when 'h3', 'b'
            puts node.text
        when 'li'
            if /\d|\D/.match(node)
                puts node.text.scan(/[\d]+\D*/).first
            end
        else
            next
    end
end

但是,它并没有改变行为。它以与之前相同的顺序处理它们(所有'h3'元素,然后是所有'li'元素,然后是所有'b'元素)。

编辑2:

好吧,我终于让它工作了。这是我的最后一组条件:

page.traverse do |node|
    case
            when 'h3' == node.name 
            puts node.text
        when 'li' == node.name 
            puts node.text.scan(/[\d]+\D*/).first if /\d|\D/.match(node)
        when 'b' == node.name
            puts node.text if (node.parent.name == 'p' or node.parent.name == 'td')
    end
end

谢谢!

4

2 回答 2

3

使用 Nokogiri,即使在您的第一个场景中,页面也不会被解析三次。Nokogiri 解析页面一次,创建内存中的 DOM,然后使用 DOM 查找所需的节点。执行多个 CSS 或 XPath 查找并不是低效的。

不过,如果您仍想一次抓取所有节点,您可以执行以下操作:

page.css('h3, li, td b').each do |node|
  case node.name
  when 'h3'
    do_something
  when 'li'
    do_something_else
  when 'b'
    do_another_thing
end

请注意,如果您需要区分 atd b和 a,p b那么此技术将不起作用。我建议单独查找。

于 2012-05-19T00:26:56.167 回答
3

您可能正在寻找遍历:

page.traverse do |node|
  case
    when ['h3', 'li'].include?(node.name) then puts node.text
    when 'b' == node.name && 'td' == node.parent.name then puts node.text[/\d+\D*/]
  end
end
于 2012-05-19T01:54:48.770 回答