如何使用 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
是这里的魔法。
如何解压非标准文件 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
是这里的魔法。