Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 如何使用Nokogiri解析HTML表?_Ruby On Rails_Ruby_Html Parsing_Nokogiri - Fatal编程技术网

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'