【问题标题】:Incompatible encodings with ruby and Nokogiri HTML与 ruby​​ 和 Nokogiri HTML 不兼容的编码
【发布时间】:2011-01-28 18:26:21
【问题描述】:

我正在使用 Nokogiri 解析外部 HTML 页面。该页面使用 ISO-8859-1 编码。我要提取的部分数据,包含一些-(破折号)html实体:

xml = Nokogiri.HTML(open("http://flybynight.com.br/agenda.php"), nil, 'ISO-8859-1')
f = xml.xpath("//div[@style='background-color:#D9DBD9; padding:15px 12px 10px 10px;']//div[@class='tit_inter_cnz']/text()")
f[0].text #=> Preview M/E/C/A \u0096 John Digweed

在最后一行,字符串应该在浏览器上用 dash 呈现。如果我将我的页面指定为 ISO-8859-1 编码,浏览器会正确呈现它,但是,我的 Sinatra 应用程序使用 UTF-8。如何在浏览器中正确显示该文本?今天被显示为一个正方形,里面有一个小数字。 我尝试了 force_encoding('ISO-8859-1'),但随后我从 Sinatra 收到了 CompatibilityError。

有什么线索吗?

[编辑] 以下是应用截图:

-> 字符编码为 UTF-8 的 Firefox

-> [Firefox 字符编码为 Western (ISO-8859-1)

值得一提的是,在上面的 ISO-8859-1 模式下,破折号显示正确,但破折号之前还有另一个不正确的字符。奇怪:(

【问题讨论】:

  • 这是迄今为止调查的一个提示:puts [xml.encoding, f[0].text.encoding] #=> ["ISO-8859-1", #<Encoding:UTF-8>] 我不确定为什么 libxml 或 Nokogiri 将来自 XML 的文本值视为 UTF-8。即使您修改 XPath 以获取 div 而不是文本节点,也会发生这种情况。即使在文档中使用#encoding: ISO-8859-1 魔术注释也会发生这种情况。
  • 没错,Phrogz。尽管文档编码为 ISO-8859-1,Nokogiri 始终以 UTF-8 格式提供节点文本
  • 您可以在调用.text 的结果上#force_encoding('ISO-8859-1'),然后干净地转换为UTF-8 ...但我还不相信您的源文档是有效的ISO-8859- 1.
  • “我还不相信您的源文档是有效的 ISO-8859-1” 我同意。破折号之前的角色是确凿的证据。正确编码的 HTML 不会有这种情况。我认为问题出在 Nokogiri 和 HTTPd 服务器的上游,无论是在渲染应用程序中还是在 HTML 生成中。复制 Word 文档并将它们粘贴到页面布局程序中就可以做到这一点,上游的不良抓取代码也是如此。
  • 是的,非常糟糕的布局,很难刮

标签: ruby encoding nokogiri


【解决方案1】:

在 Nokogiri 中解析文档后,您可以告诉它假设一个不同的encoding。试试:

require 'open-uri'
require 'nokogiri'
doc = Nokogiri::HTML((open("http://flybynight.com.br/agenda.php"), nil, 'ISO-8859-1')
doc.encoding = 'UTF-8'

我无法从这里看到该页面,以确认这解决了问题,但它适用于类似的问题。

【讨论】:

  • 等我可以看到该页面后,我会尝试查看它。
【解决方案2】:

总结:有问题的字符是来自 ISO-8859-1 的控制字符,不用于显示。

详情和调查
这是一个测试,表明您正在从 Nokogiri 和 Sinatra 获得有效的 UTF-8:

require 'sinatra'
require 'open-uri'

get '/' do
  html = open("http://flybynight.com.br/agenda.php").read
  p [ html.encoding, html.valid_encoding? ]
  #=> [#<Encoding:ISO-8859-1>, true]

  str  = html[ /Preview.+?John Digweed/ ]
  p [ str, str.encoding, str.valid_encoding? ]
  #=> ["Preview M/E/C/A \x96 John Digweed", #<Encoding:ISO-8859-1>, true]

  utf8 = str.encode('UTF-8')
  p [ utf8, utf8.encoding, utf8.valid_encoding? ]
  #=> ["Preview M/E/C/A \xC2\x96 John Digweed", #<Encoding:UTF-8>, true]

  require 'nokogiri'
  doc = Nokogiri.HTML(html, nil, 'ISO-8859-1')
  p doc.encoding
  #=> "ISO-8859-1"

  dig = doc.xpath("//div[@class='tit_inter_cnz']")[1]
  p [ dig.text, dig.text.encoding, dig.text.valid_encoding? ]
  #=> ["Preview M/E/C/A \xC2\x96 John Digweed", #<Encoding:UTF-8>, true]

  <<-ENDHTML
  <!DOCTYPE html>
  <html><head><title>Dig it!</title></head><body>
  <p>Here it comes...</p>
  <p>#{dig.text}</p>
  </body></html>
  ENDHTML
end

这可以在我的计算机上正确地提供带有 Content-Type:text/html;charset=utf-8 的内容。但是,Chrome 不会在浏览器中显示我的这个字符。

分析该响应,破折号返回的是相同的 Unicode 字节对,如上所示:\xC2\x96。这似乎是 this Unicode character,这似乎是一个奇怪的破折号。

我会把这归咎于错误的源数据,然后简单地抛出:

#encoding: UTF-8

在 Ruby 源文件的顶部,然后输入:

f = ...text.gsub( "\xC2\x96", "-" ) # Or a better Unicode character

编辑:如果您查看the browser test page for that character,您会看到(至少在 Chrome 和 Firefox 中对我而言)UTF-8 文字版本为空白,但十六进制和十进制转义版本出现。我无法理解为什么会这样,但你有它。当以原始形式呈现时,浏览器根本无法正确显示您的角色。

要么将其设为 HTML 实体,要么将其设为不同的 Unicode 破折号。无论哪种方式,都需要 gsub

编辑#2:还有一个奇怪的提示:源编码中的字符有一个十六进制字节值0x96。据我所知,this does not appear to be a printable ISO-8859-1 character。如ISO-8859-1 的官方规范所示,这属于两个非打印区域之一。

【讨论】:

  • 非常感谢您对此进行调查。我认为我们在同一页上。我正在使用 Sinatra 和 Haml 进行模板化。这正是我所看到的,但是,它没有正确呈现到屏幕上。
  • 我在其他字符(如 91)上看到了这个问题:fileformat.info/info/unicode/char/91/browsertest.htm 还不能 100% 确定这是否是浏览器问题。
  • @FelipeLima Character 0x91 也在 ISO-8859-1 的非打印范围内。它的名字是“Private Use One”,它应该告诉你一些事情。 :)
  • 好的我将此标记为答案,因为我看不到任何可行的解决方案。无论如何,我仍然无法理解为什么在浏览器上查看源页面时文本显示正确,但通过 Nokogiri 抓取时却没有。
【解决方案3】:

我从事科学手稿的出版工作,有很多破折号。您使用的破折号不是 ASCII 破折号,而是 unicode 破折号。强制 ISO 编码可能会产生改变破折号的效果。

http://www.fileformat.info/info/unicode/char/96/index.htm

该网站非常适合解决 unicode 问题。

你得到一个正方形的原因是你的浏览器可能不支持这个。它可能被正确渲染。我会保留 UTF-8 编码,如果您想制作破折号以便所有人都能看到,请将其转换为 ascii 破折号。

您可能想尝试 Iconv 将字符转换为 ASCII/UTF-8 http://craigjolicoeur.com/blog/ruby-iconv-to-the-rescue

【讨论】:

  • 浏览器支持它,因为它可以正确显示 ISO-8859-1 编码的破折号。它显示一个正方形,因为这是编码不兼容。我也试过 Iconv 没有运气:(
  • 你看到这里fileformat.info/info/unicode/char/96/browsertest.htm UTF-8 下的破折号了吗?如果不是你的字体没有它。
  • 不,我没有在 UTF-8 字段中看到破折号。但是浏览器不应该将 \u0096 自动转换为 – 吗?
猜你喜欢
  • 2014-05-28
  • 2012-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多