使用Nokogiri解析表格

3

我想使用Nokogiri解析一张表格。我的做法如下:

def parse_table_nokogiri(html)

    doc = Nokogiri::HTML(html)

    doc.search('table > tr').each do |row|
        row.search('td/font/text()').each do |col|
            p col.to_s
        end
    end

end

我有一些表格,其中的行可能是这样的:

<tr>
  <td>
     Some text
  </td>
</tr>

...而有些则具备这个功能。

<tr>
  <td>
     <font> Some text </font>
  </td>
</tr>

我的XPath表达式适用于第二种情况,但不适用于第一种情况。是否有一种XPath表达式可以用来获取单元格内部节点的文本,以便我可以处理这两种情况?


我已将更改合并到我的片段中。

def parse_table_nokogiri(html)

    doc = Nokogiri::HTML(html)
    table = doc.xpath('//table').max_by {|table| table.xpath('.//tr').length}

    rows = table.search('tr')[1..-1]
    rows.each do |row|

        cells = row.search('td//text()').collect {|text| CGI.unescapeHTML(text.to_s.strip)}
        cells.each do |col|

            puts col
            puts "_____________"

        end

    end

end

如果有多个最内层单元格怎么办?您只需要整个单元格的文本内容吗? - Mark Thomas
实际上,是的。那将是完美的。 - Mridang Agarwalla
3个回答

6

用途:

td//text()[normalize-space()]

这将选择当前节点(在您的代码中已选中的tr)的任何td子节点的所有非空白文本节点后代。

或者,如果您想选择所有文本节点的后代,无论它们是否仅为空白:

td//text()

更新:

在评论中,OP表示他得到了一个不想要的td,内容只有一个'&#160;'(也称为不间断空格)。

如果要排除内容仅由一个或多个nbsp字符组成的td,请使用以下方法:

td//text()[translate(normalize-space(), '&#160;', '')]

嗨,Dimitre,你的解决方案对我有用。你能告诉我如何取消转义HTML字符串吗?最好的方法是通过“cgi”模块吗? - Mridang Agarwalla
@Mridang-Agarwalla:如果文本中包含转义标记(非常不好的做法!)我建议重新解析该文本,然后可以保存/输出标记节点作为标记。在XSLT中,可以使用DOE(disable-output-escaping)属性,但应尽可能避免使用此方法。 - Dimitre Novatchev
似乎我可能引起了一些混淆。我想要做的是将如#&160、&nbsp等字符串转换为它们的普通形式。HTML标记是完全正常的。谢谢。 - Mridang Agarwalla
@Mridang-Agarwalla: td//text()[translate(normalize-space(), '&#160;', '')] 是一个正确的XPath表达式,我已经验证它确切地选择了所需的文本节点。很可能你有一个复制粘贴错误。请注意,translate()normalize-space()之前而不是之后--你是否修改了我的答案中的XPath表达式? - Dimitre Novatchev
我的错。我在这里复制粘贴了错误的代码。我甚至修改了我的代码,使它变得更简单。它是 cells = row.xpath("td//text()[translate(normalize-space(), '&#160;', '')]")。这是我正在使用的方法http://nokogiri.org/Nokogiri/XML/NodeSet.html#method-i-xpath,但我仍然会为每个单元格的nbsp获得额外的列。 - Mridang Agarwalla
显示剩余9条评论

3

简单:

doc.search('//td').each do |cell|
  puts cell.content
end

1

使用交替的简单(但不是DRY)方法:

require 'nokogiri'

doc = Nokogiri::HTML <<ENDHTML
<body><table><thead><tr><td>NOT THIS</td></tr></thead><tr>
  <td>foo</td>
  <td><font>bar</font></td>
</tr></table></body>
ENDHTML

p doc.xpath( '//table/tr/td/text()|//table/tr/td/font/text()' )
#=> [#<Nokogiri::XML::Text:0x80428814 "foo">,
#=>  #<Nokogiri::XML::Text:0x804286fc "bar">]

请参见XPath with optional element in hierarchy以获得更DRY的答案。

然而,在这种情况下,您可以简单地执行以下操作:

p doc.xpath( '//table/tr/td//text()' )
#=> [#<Nokogiri::XML::Text:0x80428814 "foo">,
#=>  #<Nokogiri::XML::Text:0x804286fc "bar">]

请注意,您的表格结构(以及上面的我的)没有显式的元素,这在XHTML中是无效的。但是,考虑到您上面的显式 > ,我假设您有一个理由这样做。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接