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

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

起因在这里:http://ruby-china.org/topics/2459

我写了一个脚本把我网站(rubyer.me)相关所有百度快照抓取过来了。这样做的优点就是SEO友好,而且有些朋友链接了我的博客,这样做不会导致链接丢失。 这个脚本实现的功能:

抓取网站相关的百度快照,并根据快照的路径生成相应文件。

现在只有一个问题:

The Fu*king encoding

我试了GB2312, GBK, UTF-8之间的各种转换结合。 恶心的是总有半的能正常编码,另一部分乱码。奇迹般的出现

#<:invalidbytesequenceerror: on utf-8> #Encoding:ASCII-8BIT怎么会有这个编码?

代码写的急, 有点乱,但能正常运行,希望大大们有兴趣可以试一下,复制粘贴即可。

源码由于太长,贴出来影响排版,就麻烦去github看吧: https://github.com/camelsong/cache_spider/blob/master/baidu_cache_spider.rb

听说C处理UTF-8有一些hacks,哪位了解吗?ruby相应的是不是也需要呢?

共收到 35 条回复
16
page = Nokogiri::HTML(open(url))
page.encoding = "bg2312"

试试这个

558

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

15

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

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

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

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



558

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

15

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

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

96

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

273

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

16

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

1342

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

96

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

96

我把23行改为

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

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

96

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

558

#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处理编码还是硬伤吗???

96

最后改了下xpath为/html/body[2]就通了~

汇总下:

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

#14楼 @lanisle 谢谢关注。我这里用/html/body/div[3]可以抓到的呀?/html/body[2]抓取的就是空的了

947

Nokogiri可以直接取到页面的编码格式 假设response是获取的网页内容

charset = Nokogiri::HTML(response).meta_encoding  

转码的方法可以如下

一、 Nokogiri来转码(他会转成当前代码的编码格式,这个有待于确定)

Nokogiri::HTML(response,nil,charset)  

二、 Ruby自带的方法来转码转成utf8

Iconv.iconv(charset.upcase,"UTF-8",response)  

96

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

273

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

De6df3

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

558

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

947

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

8

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

558

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

558

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

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

然后就中止了

8

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

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

558

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

8

可以这样:

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

558

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

16

#24楼 @hooopo 谢谢炮哥

16

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

8949

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

10316

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

3454

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

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册