使用Nokogiri在随机选择的标签</span>之间选择所有<span>标签

时间:2012-09-27 14:37:52

标签: ruby xpath nokogiri

我正在尝试使用Nokogiri和Ruby来解析一些HTML,而且我正在做一些我想要发生的事情。我的示例文件看起来像这样:

<div id="main-body">
  <p>
    <span>First Text</span>
    <span>Second Text</span>
    <span>Third Text</span>
    <span>Fourth Text</span>
    <br>
    <span>Fifth Text</span>
    <span>Sixth Text</span>
    <span>Seventh Text</span>
    <br>
    <span>Eight Text</span>
    <span>Ninth Text</span>
    <br>
  </p>
</div

我正在尝试找到一种方法,只选择随机中断标记之间或第一次中断之前的文本。

基本上我希望每次通过时输出都不同。因此,示例输出将是例如

=>First Text
=>Second Text
=>Third Text
=>Fourth Text

一次,然后,也许下一次它会在第2和第3分钟之间得到这些东西

=>Eight Text
=>Ninth Text

我在想如果我可以计算HTML中的中断数量,那么我可以使用rand()选择一个随机数,然后打印文本,直到我点击下一个,但我不能完全理解它正确。

到目前为止,我的代码是将整个页面加载到Nokogiri类型并尝试从那里进行解析。

doc = Nokogiri::HTML(open(targeturl))
puts doc.xpath("./br").length #gives me the count of break tags in the document

从那里我可以将休息分配给一个变量然后一个接一个地访问它们但是我不确定去哪里或者我是否从这里开始走上正确的轨道并且我觉得我只是让人困惑我自己在这一点上:/

4 个答案:

答案 0 :(得分:1)

您可以使用count()preceding-sibling根据前面的br标记数量获取节点。

puts doc.xpath("//span[count(preceding-sibling::br)=0]")
#=> <span>First Text</span>
#=> <span>Second Text</span>
#=> <span>Third Text</span>
#=> <span>Fourth Text</span>

puts doc.xpath("//span[count(preceding-sibling::br)=1]")
#=> <span>Fifth Text</span>
#=> <span>Sixth Text</span>
#=> <span>Seventh Text</span>

puts doc.xpath("//span[count(preceding-sibling::br)=2]")
#=> <span>Eight Text</span>
#=> <span>Ninth Text</span>

要获得随机选择,只需随机化前一兄弟的数量:

puts doc.xpath("//span[count(preceding-sibling::br)=#{rand(doc.xpath("//br").length)}]")

答案 1 :(得分:1)

另一种选择是slice_before:

doc.search('p *').slice_before{|x| x.name == 'br'}.each do |slice|
  puts '*', slice.select{|x| x.name == 'span'}
end

答案 2 :(得分:0)

这是我的解决方案,如果您只想要span元素的内容(获取整个节点,只需删除地图操作):

require 'nokogiri'

doc = Nokogiri::XML(File.read("test.xml"))

def get_spans(doc)
    br_list = doc.css("br")
    br = br_list[rand(br_list.size)]

    span_list = []
    span = br.previous_element

    while span && span.name == "span"
         span_list << span
         span = span.previous_element
    end

    span_list.reverse.map(&:content)
end

p get_spans(doc)

答案 3 :(得分:0)

这是一个单一的XPath单行表达式,它选择$k之前br之前的所有文本节点,而不是$k - 第1 {{1}之前的文本节点}

br

因此,如果您在/*/p/br[2]/preceding-sibling::*[count(preceding-sibling::br)=1]/text() 区间内有一个随机k,那么您只需将其替换为上述Xpath表达式并对其进行评估。

基于XSLT的验证

[1, count(/*/p/br)]

在提供的XML文档上应用此转换时:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output  method="text"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "/*/p/br[2]/preceding-sibling::*[count(preceding-sibling::br)=1]/text()
     "/>
 </xsl:template>
</xsl:stylesheet>

评估XPath表达式,并将此评估的结果(所有选定的文本节点)复制到输出

<div id="main-body">
    <p>
        <span>First Text</span>
        <span>Second Text</span>
        <span>Third Text</span>
        <span>Fourth Text</span>
        <br/>
        <span>Fifth Text</span>
        <span>Sixth Text</span>
        <span>Seventh Text</span>
        <br/>
        <span>Eight Text</span>
        <span>Ninth Text</span>
        <br/>
    </p>
</div>