如何使用 Nokogiri 抓取非标准 HTML

How to scrape non-standard HTML with Nokogiri

如何解压非标准文件 HTML:

<body>
    <div class="open">
        <div style='style'>Raw name 1</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>         
        <div style='style'>Raw name 5</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>
    </div>
</body>

我想得到类似于以下的结果:

['Raw name 1', Text_1, Text_2, Text_3, Text_4, Text_5]
...
['Raw name 5', Text_1, Text_2, Text_3, Text_4, Text_5]

我试图对这个例子做一些事情,但没有任何反应。

是否可以从中获取我想要的信息HTML?

如果我理解正确的话这可能对你有用

require 'nokogiri'
body = <<-BODY 
<body>
    <div class="open">
        <div style='style'>Raw name 1</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>         
        <div style='style'>Raw name 5</div>
        <p>Text_1</p>
        <p>Text_2</p>
        <p>Text_3</p>
        <p>Text_4</p>
        <p>Text_5</p>
    </div>
</body>   
BODY

doc = Nokogiri::HTML(body)
doc.xpath('//body/div').children.each_with_object({}) do |node,obj|
    text = node.text.strip
    obj[text] = [] if node.name == 'div'
    obj[obj.keys.last] << text if node.name == 'p'
end
#=> {"Raw name 1"=>["Text_1", "Text_2", "Text_3", "Text_4", "Text_5"], 
#     "Raw name 5"=>["Text_1", "Text_2", "Text_3", "Text_4", "Text_5"]}

步骤:

  • 这是从 xpath 到第一个 div (doc.xpath('//body/div'))
  • 然后将那个 div 的每个 child (.children) 连同 object (.each_with_object({}) do |node,obj|) 一起传递给块,在这种情况下作为累加器。
  • 然后它为每个 div 标签添加一个键并将其分配给一个空数组 (obj[text] = [] if node.name == 'div')。
  • 它使用以下 p 标记 (obj[obj.keys.last] << text if node.name == 'p')
  • 填充最后一个键

结果是 Hash,其中键是 divs,值是以下 p 标签文本的 Array,直到到达下一个div

我会这样做:

require 'nokogiri'

doc = Nokogiri::HTML(<<EOT)
<body>
    <div class="open">
        <div style='style'>Raw name 1</div>
        <p>Text_1</p>
        <p>Text_2</p>         
        <div style='style'>Raw name 5</div>
        <p>Text_1</p>
        <p>Text_2</p>
    </div>
</body>
EOT

doc.at('.open').elements.slice_before { |e| e.name == 'div' }.map { |ary|
  ary.map(&:text)
}
# => [["Raw name 1", "Text_1", "Text_2"], ["Raw name 5", "Text_1", "Text_2"]]

稍微分解一下:

doc.at('.open').elements.map(&:name) # => ["div", "p", "p", "div", "p", "p"]
doc.at('.open').elements.slice_before { |e| e.name == 'div' }.map { |a| a.map(&:name) } # => [["div", "p", "p"], ["div", "p", "p"]]

elements and slice_before 是这里的魔法。