我正在用 Ruby (1.9) 编写一个爬虫,它使用来自许多随机站点的大量 HTML。
在尝试提取链接时,我决定只使用 .scan(/href="(.*?)"/i)
而不是 nokogiri/hpricot(主要加速)。问题是我现在收到很多“invalid byte sequence in UTF-8
”错误。
据我了解,net/http
库没有任何编码特定选项,并且进来的内容基本上没有正确标记。
实际处理传入数据的最佳方式是什么?我尝试使用 .encode
设置替换和无效选项,但到目前为止没有成功...
'U*'
撤消 'C*'
吗?
在 Ruby 1.9.3 中,可以使用 String.encode 来“忽略”无效的 UTF-8 序列。这是一个适用于 1.8 (iconv) 和 1.9 (String#encode) 的代码段:
require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
else
ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
file_contents = ic.iconv(file_contents)
end
或者,如果您的输入非常麻烦,您可以进行从 UTF-8 到 UTF-16 并返回到 UTF-8 的双重转换:
require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
file_contents.encode!('UTF-8', 'UTF-16')
else
ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
file_contents = ic.iconv(file_contents)
end
接受的答案或其他答案对我有用。我找到了建议的 this post
string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
这解决了我的问题。
fooo??? {!@#$%^&*()_+}< /p>
我目前的解决方案是运行:
my_string.unpack("C*").pack("U*")
这至少会摆脱我的主要问题的异常
valid_encoding?
结合使用,这似乎可以检测到何时出现问题。 val.unpack('C*').pack('U*') if !val.valid_encoding?
。
\xB0
转换回度数符号。即使 valid_encoding?
恢复为真,但我仍然检查它是否不正确,并使用上面 Amir 的答案去除有问题的字符:string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
。我也尝试过 force_encoding
路线,但失败了。
尝试这个:
def to_utf8(str)
str = str.force_encoding('UTF-8')
return str if str.valid_encoding?
str.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '')
end
我建议您使用 HTML 解析器。只要找到最快的。
解析 HTML 并不像看起来那么容易。
浏览器在 UTF-8 HTML 文档中解析无效的 UTF-8 序列,只需加上“�”符号。因此,一旦 HTML 中的无效 UTF-8 序列被解析,结果文本就是一个有效的字符串。
即使在属性值内部,您也必须解码 HTML 实体,如 amp
这是一个很好的问题,它总结了为什么不能使用正则表达式可靠地解析 HTML:RegEx match open tags except XHTML self-contained tags
attachment = file.read
begin
# Try it as UTF-8 directly
cleaned = attachment.dup.force_encoding('UTF-8')
unless cleaned.valid_encoding?
# Some of it might be old Windows code page
cleaned = attachment.encode( 'UTF-8', 'Windows-1252' )
end
attachment = cleaned
rescue EncodingError
# Force it to UTF-8, throwing out invalid bits
attachment = attachment.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)
end
这似乎有效:
def sanitize_utf8(string)
return nil if string.nil?
return string if string.valid_encoding?
string.chars.select { |c| c.valid_encoding? }.join
end
我遇到了字符串,它混合了英语、俄语和其他一些字母,这导致了异常。我只需要俄语和英语,目前这对我有用:
ec1 = Encoding::Converter.new "UTF-8","Windows-1251",:invalid=>:replace,:undef=>:replace,:replace=>""
ec2 = Encoding::Converter.new "Windows-1251","UTF-8",:invalid=>:replace,:undef=>:replace,:replace=>""
t = ec2.convert ec1.convert t
虽然 Nakilon 的解决方案有效,但至少就克服错误而言,在我的情况下,我将这个源自 Microsoft Excel 的奇怪的 f-ed 字符转换为 CSV,该字符在 ruby 中注册为(get this)西里尔字母 K,其中ruby 是粗体 K。为了解决这个问题,我使用了“iso-8859-1”即。 CSV.parse(f, :encoding => "iso-8859-1")
,这将我怪异的 deaky 西里尔字母 K 变成了更易于管理的 /\xCA/
,然后我可以使用 string.gsub!(/\xCA/, '')
将其删除
在使用 scan
之前,请确保所请求页面的 Content-Type
标头是 text/html
,因为可能存在指向未以 UTF-8 编码的图像等内容的链接。如果您在 <link>
元素中选择了 href
,则该页面也可能是非 html。如何检查这取决于您使用的 HTTP 库。然后,确保结果只是带有 String#ascii_only?
的 ascii(不是 UTF-8,因为 HTML 应该只使用 ascii,否则可以使用实体)。如果这两个测试都通过,则使用 scan
是安全的。
如果您不“关心”数据,则可以执行以下操作:
search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"
我只是使用 valid_encoding?
来通过它。我的是一个搜索字段,所以我一遍又一遍地发现同样的怪事,所以我使用了类似的东西:只是为了让系统不会崩溃。由于我不控制用户体验在发送此信息之前自动验证(例如自动反馈说“假装!”)我可以将其接收,剥离并返回空白结果。
file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
file_contents.encode!('UTF-8', 'UTF-16')
force_encoding
的选项。如果您将 ISO8859-1 读取为 UTF-8(因此该字符串包含无效的 UTF-8),那么您可以使用 the_string.force_encoding("ISO8859-1") 将其“重新解释”为 ISO8859-1 并正常工作使用该字符串的真实编码。.encode('UTF-8')
是无操作的,并且不会运行任何检查。 Ruby Core Documentation for encode。但是,首先将其转换为 UTF-16 会强制运行所有无效字节序列检查,并根据需要进行替换。