Ruby on rails 如何使用Nokogiri解析HTML表?
我试图解析一个表,但我不知道如何从中保存数据。我希望将每行中的数据保存为:Ruby on rails 如何使用Nokogiri解析HTML表?,ruby-on-rails,ruby,html-parsing,nokogiri,Ruby On Rails,Ruby,Html Parsing,Nokogiri,我试图解析一个表,但我不知道如何从中保存数据。我希望将每行中的数据保存为: ['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452] 样本表为: html = <<EOT <table class="open"> <tr> <th>Table name</th> <th>Column name 1</th
['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452]
样本表为:
html = <<EOT
<table class="open">
<tr>
<th>Table name</th>
<th>Column name 1</th>
<th>Column name 2</th>
<th>Column name 3</th>
<th>Column name 4</th>
<th>Column name 5</th>
</tr>
<tr>
<th>Raw name 1</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
.
.
.
<tr>
<th>Raw name 5</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
</table>
EOT
html=th')。文本
cell_data=table.css('tr>td').text
原始名称=table.css('tr>th')。文本
@tableArray问题的关键在于,对多个结果调用#text
,将返回每个元素的#text
的串联
让我们检查每个步骤的作用:
# Finds all <table>s with class open
# I'm assuming you have only one <table> so
# you don't actually have to loop through
# all tables, instead you can just operate
# on the first one. If that is not the case,
# you can use a loop the way you did
tables = doc.css('table.open')
# The text of all <th>s in <tr> one in the table
title = table.css('tr[1] > th').text
# The text of all <td>s in all <tr>s in the table
# You obviously wanted just the <td>s in one <tr>
cell_data = table.css('tr > td').text
# The text of all <th>s in all <tr>s in the table
# You obviously wanted just the <th>s in one <tr>
raw_name = table.css('tr > th').text
#查找类打开的所有
#我想你只有一个这样的
#实际上,你不必循环
#所有的桌子,你可以直接操作
#第一个。如果不是这样的话,
#你可以像以前那样使用循环
tables=doc.css('table.open')
#表中一个表中所有s的文本
title=table.css('tr[1]>th')。text
#表中所有s中所有s的文本
#很明显,你只想要一个s
cell_data=table.css('tr>td').text
#表中所有s中所有s的文本
#很明显,你只想要一个s
原始名称=table.css('tr>th')。文本
现在我们知道了问题所在,下面是一个可能的解决方案:
html = <<EOT
<table class="open">
<tr>
<th>Table name</th>
<th>Column name 1</th>
<th>Column name 2</th>
<th>Column name 3</th>
<th>Column name 4</th>
<th>Column name 5</th>
</tr>
<tr>
<th>Raw name 1</th>
<td>1001</td>
<td>1002</td>
<td>1003</td>
<td>1004</td>
<td>1005</td>
</tr>
<tr>
<th>Raw name 2</th>
<td>2001</td>
<td>2002</td>
<td>2003</td>
<td>2004</td>
<td>2005</td>
</tr>
<tr>
<th>Raw name 3</th>
<td>3001</td>
<td>3002</td>
<td>3003</td>
<td>3004</td>
<td>3005</td>
</tr>
</table>
EOT
html=[[“原始名称1”、“1001”、“1002”、“1003”、“1004”、“1005”],
#[“原始名称2”、“2001”、“2002”、“2003”、“2004”、“2005”],
#[“原始名称3”、“3001”、“3002”、“3003”、“3004”、“3005”]]
#如果你想把它们结合起来
text_所有行。每个行作为文本|
p column_names.zip(行作为文本)。to_h
结束#=>
#{“表名”=>“原始名称1”、“列名1”=>“1001”、“列名2”=>“1002”、“列名3”=>“1003”、“列名4”=>“1004”、“列名5”=>“1005”}
#{“表名”=>“原始名称2”,“列名1”=>“2001”,“列名2”=>“2002”,“列名3”=>“2003”,“列名4”=>“2004”,“列名5”=>“2005”}
#{“表名”=>“原始名称3”,“列名1”=>“3001”,“列名2”=>“3002”,“列名3”=>“3003”,“列名4”=>“3004”,“列名5”=>“3005”}
您想要的输出是无意义的:
['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452]
# ~> -:1: Invalid octal digit
# ~> ['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452]
我假设你想要带引号的数字
在剥离使代码无法工作的内容,并将HTML简化为更易于管理的示例后,再运行它:
require 'nokogiri'
html = <<EOT
<table class="open">
<tr>
<th>Table name</th>
<th>Column name 1</th>
<th>Column name 2</th>
</tr>
<tr>
<th>Raw name 1</th>
<td>2,094</td>
<td>0,017</td>
</tr>
<tr>
<th>Raw name 5</th>
<td>2,094</td>
<td>0,017</td>
</tr>
</table>
EOT
doc = Nokogiri::HTML(html)
tables = doc.css('table.open')
tables_data = []
tables.each do |table|
title = table.css('tr[1] > th').text # !> assigned but unused variable - title
cell_data = table.css('tr > td').text
raw_name = table.css('tr > th').text
tables_data << [cell_data, raw_name]
end
首先要注意的是,您没有使用title
,尽管您为其分配了权限。这可能是在清理代码时发生的
css
与search
和xpath
一样,返回一个节点集,它类似于一个节点数组。在节点集上使用text
或internal_text
时,它将返回连接到单个字符串中的每个节点的文本:
获取包含的所有节点对象的内部文本
这就是它的行为:
require 'nokogiri'
doc = Nokogiri::HTML('<html><body><p>foo</p><p>bar</p></body></html>')
doc.css('p').text # => "foobar"
这可以归结为:
doc.css('p').map(&:text) # => ["foo", "bar"]
另见“”一节
当与节点一起使用时,文档中会提到内容
、文本
和内部文本
:
返回此节点的内容
相反,您需要查找单个节点的文本:
require 'nokogiri'
html = <<EOT
<table class="open">
<tr>
<th>Table name</th>
<th>Column name 1</th>
<th>Column name 2</th>
<th>Column name 3</th>
<th>Column name 4</th>
<th>Column name 5</th>
</tr>
<tr>
<th>Raw name 1</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
<tr>
<th>Raw name 5</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
</table>
EOT
tables_data = []
doc = Nokogiri::HTML(html)
doc.css('table.open').each do |table|
# find all rows in the current table, then iterate over the second all the way to the final one...
table.css('tr')[1..-1].each do |tr|
# collect the cell data and raw names from the remaining rows' cells...
raw_name = tr.at('th').text
cell_data = tr.css('td').map(&:text)
# aggregate it...
tables_data += [raw_name, cell_data]
end
end
您可以找出如何将引用的数字强制转换为Ruby可以接受的小数,或者根据需要操纵内部数组。我假设您从这里借用了一些代码或任何其他相关引用(或者很抱歉添加了错误的引用)-
但是,如果要捕获所有行,可以更改以下代码-
希望这能帮助你解决你的问题
doc = Nokogiri::HTML(open(html), nil, 'UTF-8')
# We need .open tr, because we want to capture all the columns from a specific table's row
@tablesArray = doc.css('table.open tr').reduce([]) do |array, row|
# This will allow us to create result as this your illustrated one
# ie. ['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452]
array << row.css('th, td').map(&:text)
end
render template: 'scrape_krasecology'
doc=Nokogiri::HTML(开放(HTML),无“UTF-8”)
#我们需要.open tr,因为我们想从特定表的行中捕获所有列
@tableArray=doc.css('table.open tr')。reduce([])do |数组,行|
#这将使我们能够创建这样一个结果,即您的图示结果
#即[原始名称1',2094,0017,0098,0113,0452]
请将您的代码减少到演示问题所需的最低限度。在问题本身中提供一个简单的HTML示例来演示问题。不要要求我们转到页面来提取HTML或构建测试您的HTML所需的周围代码。读“,”和“铁皮人”,谢谢。我已经更新了我的代码。相信现在它看起来更好了吗?一般来说,对于研究这个主题的人来说,一般信息:虽然它更可读,但它仍然不可测试,甚至不可运行。这就是上面提到的链接的要点;我们需要能够测试您的代码以复制问题。我们可以删除一些代码并使其可运行,但我们不必这样做。您的HTML没有任何div,但您的代码显示您正在尝试查找它们。什么是表
?。为什么有render template
和两个终止的end
s?我们必须把那些东西拿出来测试。显示返回的数据的最小样本减去自定义类的使用。此外,您所需的输出格式不太可能提供您想要的结果。曾经将它粘贴到IRb中,看看Ruby认为它意味着什么。编程是一门非常严格的科学;你必须用同样严格的术语来描述它(问问题)。非常感谢你的回答和解释!答案很有用,对我很有帮助!不要使用css(…)。首先使用at_css(…)
或它的一个同级。它可读性更强,篇幅更短。另外,不要养成使用css(“…”).text的习惯。它会咬你很厉害。有关更多信息,请参阅。谢谢。它真的帮助了我。
doc.css('p').map{ |node| node.text } # => ["foo", "bar"]
doc.css('p').map(&:text) # => ["foo", "bar"]
require 'nokogiri'
html = <<EOT
<table class="open">
<tr>
<th>Table name</th>
<th>Column name 1</th>
<th>Column name 2</th>
<th>Column name 3</th>
<th>Column name 4</th>
<th>Column name 5</th>
</tr>
<tr>
<th>Raw name 1</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
<tr>
<th>Raw name 5</th>
<td>2,094</td>
<td>0,017</td>
<td>0,098</td>
<td>0,113</td>
<td>0,452</td>
</tr>
</table>
EOT
tables_data = []
doc = Nokogiri::HTML(html)
doc.css('table.open').each do |table|
# find all rows in the current table, then iterate over the second all the way to the final one...
table.css('tr')[1..-1].each do |tr|
# collect the cell data and raw names from the remaining rows' cells...
raw_name = tr.at('th').text
cell_data = tr.css('td').map(&:text)
# aggregate it...
tables_data += [raw_name, cell_data]
end
end
tables_data
# => ["Raw name 1",
# ["2,094", "0,017", "0,098", "0,113", "0,452"],
# "Raw name 5",
# ["2,094", "0,017", "0,098", "0,113", "0,452"]]
doc = Nokogiri::HTML(open(html), nil, 'UTF-8')
# We need .open tr, because we want to capture all the columns from a specific table's row
@tablesArray = doc.css('table.open tr').reduce([]) do |array, row|
# This will allow us to create result as this your illustrated one
# ie. ['Raw name 1', 2,094, 0,017, 0,098, 0,113, 0,452]
array << row.css('th, td').map(&:text)
end
render template: 'scrape_krasecology'