起因在这里:http://ruby-china.org/topics/2459
我写了一个脚本把我网站 (rubyer.me) 相关所有百度快照抓取过来了。这样做的优点就是 SEO 友好,而且有些朋友链接了我的博客,这样做不会导致链接丢失。 这个脚本实现的功能:
抓取网站相关的百度快照,并根据快照的路径生成相应文件。
现在只有一个问题:
The Fu*king encoding
我试了 GB2312,GBK,UTF-8 之间的各种转换结合。 恶心的是总有半的能正常编码,另一部分乱码。奇迹般的出现
# #Encoding:ASCII-8BIT怎么会有这个编码?
代码写的急,有点乱,但能正常运行,希望大大们有兴趣可以试一下,复制粘贴即可。
源码由于太长,贴出来影响排版,就麻烦去 github 看吧: https://github.com/camelsong/cache_spider/blob/master/baidu_cache_spider.rb
听说 C 处理 UTF-8 有一些 hacks,哪位了解吗?ruby 相应的是不是也需要呢?
我的博客转换过很 blog 程序,数据来来去去,也乱码不少回,在上次转到 octopress 时出现了乱码问题
https://github.com/huobazi/typecho-to-octopress
尝试很多次用下面的代码解决了:
converter = Iconv.new 'UTF-8//IGNORE', 'UTF-8'
p converter.iconv your_text
我把 23 行改为
str = str.encode! 'utf-8', 'gb2312', {:invalid => :replace}
跑是可以跑通,可是抓取出来的 html 文件内容都是 6 字节的空白文本……
#3 楼 @huobazi 百度的源码的确是“GB2312”的,Iconv.new 'UTF-8//IGNORE', 'UTF-8'
的结果是Ruby-×¢Ruby,Railsߪעܼ - Part 7
全乱了。
#7 楼 @ruby_sky Iconv.iconv("UTF-8//IGNORE","GB2312//IGNORE"
。这样转换只能部分成功,结果类似Ruby迷-关注Ruby,Rails擢注芗释 - Part 7
让人非常纠结。
#10 楼 @lanisle force_encode
只是显示指定字符串的编码类型,其实并没有改变字符串内容。与 Iconv 指定编码类型转换无异。
目前最好的方法还是使用 Iconv("UTF-8//IGNORE", "GB2312//IGNORE", str),GBK 也试过,但结果变成了
“马想到了 Helper 中有 strip_tags 这溉L可以过滤掉字符串中所有 HTML 标签 c 斋 helper 捉专默认只能在 MVC 层中的 View 层使“
Ruby 1.9 中不再推荐使用Iconv
,把Iconv.iconv('gbk', 'utf-8', string)
变成了string.encode('gbk', 'utf-8')
,但是换汤不换药的东西。
伤不起呀,不禁感叹,难道 Ruby 处理编码还是硬伤吗???
最后改了下 xpath 为/html/body[2]
就通了~
汇总下:
str = str.encode! 'utf-8', 'gb2312', {:invalid => :replace}
/html/body[2]
Nokogiri 可以直接取到页面的编码格式 假设 response 是获取的网页内容
charset = Nokogiri::HTML(response).meta_encoding
转码的方法可以如下
一、 Nokogiri 来转码(他会转成当前代码的编码格式,这个有待于确定)
Nokogiri::HTML(response,nil,charset)
二、 Ruby 自带的方法来转码转成 utf8
Iconv.iconv(charset.upcase,"UTF-8",response)
#22 楼 @hooopo #21 楼 @chinacheng #19 楼 @huacnlee 感谢三位大牛这么热心,我把执行结果简单整理了一下:
法一、结合 meta_encoding 和 Iconv
#coding: utf-8
require 'open-uri'
require 'nokogiri'
require 'iconv'
#这个url是百度一个快照的地址,直接拿来做例子。
url = "http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece763105690365203c0743ca08f426284cd15c6790a120131b6e667690d44809e222615ea141cbcff&p=817bc45b87934eac5fa8c7710a0d&user=baidu&fm=sc&query=site%3Arubyer%2Eme&qid=b351e91d4dc50477&p1=2"
response = open(url).read
response = Nokogiri::HTML.parse(response)
response = Iconv.conv("utf-8", "gb2312", response)
puts respons
执行结果:
➜ cache_spider git:(master) ✗ ruby temp.rb
temp.rb:11:in conv': "\xF8վ\xB5ļ\xB4ʱҳ\xC3档)\r\n\r\n\r\n"... (Iconv::IllegalSequence) from temp.rb:11:in
法二、直接猜编码为 utf-8
# 省略url前代码
response = open(url).read
response = Nokogiri::HTML.parse(response,nil,"utf-8")
puts response
能输出 HTML,但基本乱码,类似:
ÒÔÇ°¶Ômodel¸³Ä¬ÈÏÖµÒ»Ö±ÊÇͨ¹ýÐÞ¸ÄmigrationÀ´ÊµÏÖ£¬¿´ÁËruby-chinaµÄÔ ´Âëºó¸Ð¾õÄÇÑùдÈç¹ûǨÒÆÊý¾Ý¿âʱ»á±È½ÏÂé·³£¬»¹ÊǷŵ½modelÀïºÏÊÊ¡£ÓÚÊǸøÏîÄ¿µÄmodelÌí¼ÓÁËbefore_create£¬È»ºóÔËÐÐrake db:s...
法三、直接猜编码为 utf-8
# 省略url前代码
response = open(url)
response = Nokogiri::HTML.parse(response,nil,"gb2312")
#response = Nokogiri::HTML.parse(response)
#response = Iconv.conv("utf-8", response.meta_encoding, response)
puts response
输出:
➜ cache_spider git:(master) ✗ ruby temp.rb output error : unknown encoding gb2312
法四、试了以上 3 种方法都失败后,我才想自己处理编码。
def convert_encoding(source_encoding, destination_encoding, str)
ec = Encoding::Converter.new(source_encoding, destination_encoding)
begin
ec.convert(str)
rescue Encoding::UndefinedConversionError
p $!.error_char.dump
p $!.error_char.encoding
rescue Encoding::InvalidByteSequenceError
p $!
p $!.error_bytes.dump if $!.error_bytes
p $!.readagain_bytes.dump if $!.readagain_bytes
end
str
end
试了 gbk, utf-8, gb2312 各种组合,都不完美。
html = open(url).read
html.force_encoding("gbk")
html.encode!("utf-8")
doc = Nokogiri::HTML.parse html
doc.css("body")
未发现乱码
html = open(url).read
html = Iconv.conv("utf-8", "gbk", html)
doc = Nokogiri::HTML.parse html
doc.css("body")
可以这样:
encode!("utf-8", :undef => :replace, :replace => "?", :invalid => :replace)
遇到过类似的问题,有些中文网页指定的编码是 gb2312,其实是 GB18030,doc = Nokogiri::HTML(open(url), nil, "GB18030") 就 ok 啦