Ruby [求助] Nokogiri 抓取百度快照乱码处理

camel · 2012年04月09日 · 最后由 xwf286 回复于 2016年01月21日 · 13423 次阅读

起因在这里: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 相应的是不是也需要呢?

page = Nokogiri::HTML(open(url))
page.encoding = "bg2312"

试试这个

#1 楼 @ywencn 试过 gb2312,部分乱码,只有个别能正常显示

我的博客转换过很 blog 程序,数据来来去去,也乱码不少回,在上次转到 octopress 时出现了乱码问题

https://github.com/huobazi/typecho-to-octopress

尝试很多次用下面的代码解决了:

converter = Iconv.new 'UTF-8//IGNORE', 'UTF-8'
p converter.iconv  your_text



#3 楼 @huobazi 这个 IGNORE 会丢失很多吧?

#4 楼 @camel 我当时碰到的是评论中每个人的名字前面都有几个乱码,我就用 IGNORE 了

还有变态的情况,数据库里存的已经是 � 了,我就直接 replace 成空格了

听说百度的编码似乎不一致,很奇怪。

title = Iconv.iconv("UTF-8//IGNORE","GB2312//IGNORE",item.title)??

#2 楼 @camel 我的意思是你不直接改 string 的编码,直接用 Nokogiri 的.encoding 试试

那里面还有些日文的编码,直接上 Shift_JIS 还不好使都

我也弄过,记得是用 force encode 解决的,回家看看代码。

我把 23 行改为

str = str.encode! 'utf-8', 'gb2312', {:invalid => :replace}

跑是可以跑通,可是抓取出来的 html 文件内容都是 6 字节的空白文本……

我是这么搞定的 Nokogiri::HTML(open(url), nil, "GB18030")

#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]就通了~

汇总下:

  1. 23 行修改为str = str.encode! 'utf-8', 'gb2312', {:invalid => :replace}
  2. get_cache 中的 xpath 改为 /html/body[2]

#14 楼 @lanisle 谢谢关注。我这里用/html/body/div[3]可以抓到的呀?/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)  

#15 楼 @camel 我这边不行,div[3] 会受 cache 的 doctype 影响,所以要用 body[2]。 我本地的 nokogiri 版本是 1.5.2

#13 楼 @camel 不会吧。难道对特殊数据格式的就转换不了了?真坑爹。。

#16 楼 的方法一正解,Nokogiri 自带编码出来功能,你只需要告诉它对方网页是什么编码就可以了,无需手动处理编码。

#19 楼 @huacnlee 就是因为这种方法不行才手动处理编码的。 方法一因为遇到不能处理字符,直接会报错。

#20 楼 @camel 一年前我弄过这么个一个东西,刚开始遇到了这个个问题,当时我发现错误的原因是获取源文件的编码格式出现了问题,后来我用的是第二种方法解决的,前提是获取正确的源文件的编码格式

给个链接哇 怎么可能处理不了!

#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 各种组合,都不完美。

#24 楼 @hooopo 这个方法好,大部分可以,但有一个网页解析时出来:

in `encode!': "\x81" followed by "7" on GBK (Encoding::InvalidByteSequenceError)

然后就中止了

#25 楼 @camel iconv 也没有问题的:

html = open(url).read
html = Iconv.conv("utf-8", "gbk", html)
doc = Nokogiri::HTML.parse html
doc.css("body")

#26 楼 @hooopo 看出区别了,你是在Nokogiri::HTML.parse前转换编码。而我是先 parse,然后才转码。

可以这样:

encode!("utf-8", :undef => :replace, :replace => "?", :invalid => :replace)

#28 楼 @hooopo 已经全部恢复了,再次感谢

#31 楼 @hooopo 今天刚好遇到,没搞定,google 到这里来了。

#31 楼 @hooopo 我也来挖个坟,我使用此方法在.rb 中没有问题,但是放到.rake 文件中就没办法解决。

Google 到这里,我还是没有解决。。。

遇到过类似的问题,有些中文网页指定的编码是 gb2312,其实是 GB18030,doc = Nokogiri::HTML(open(url), nil, "GB18030") 就 ok 啦

lrbnew nokogiri 采集网页的乱码问题 提及了此话题。 04月03日 10:57
需要 登录 后方可回复, 如果你还没有账号请 注册新账号