1、乱码的解决方法
根据原因来解决方法,就非常简单了。
(1) 确定源网页的编码A
编码A往往在网页中的三个位置,http header的content、网页的meta charset中、网页头中Document定义中。在获取源网页编码时,依次判断下这三部分数据即可,从前往后,优先级亦是如此。
理论上这样做是对的,但国内一些网站确是很不符合规范,比如写的gbk,实际是utf-8,有的是写的utf-8,但实际是gbk,当然这是很少的一批网站,但确实存在。所以在确定网页编码时,应该对该特殊情况做特别处理,如中文检查、默认编码等策略。
还有一种情况,是以上三者中均没有编码信息,则一般采用cpdetector等第三方网页编码智能识别工具来做,其原理即为统计字节数组的特征来概率计算得出实际编码,有一定的准确率,但我实际的时候发现,其准确率还是很有限的。
但综合上述的三种编码确认方式后,几乎可以完全解决中文乱码问题,在我基于nutch1.6二次开发的网络爬虫系统中,编码正确经统计可以达到99.99%,也证明了上述方法策略的可行性。
(2)程序通过编码B对源网页数据还原
显然,这里的B是要和A相等的,在java中,如得到的源网页的字节数组为source_byte_array,那么经过转换为String str=new String(source_byte_array,B);即在内存上这些字节数组对应的字符是正确编码和可显示的,此时的打印输出结果是正常的,此步骤往往用于debug或是控制台输出做测试。
(3) 统一转码
网络爬虫系统数据来源很多,不可能使用数据时,再转化为其原始的数据,假使这样做是很废事的。所以一般的爬虫系统都要对抓取下来的结果进行统一编码,从而在使用时做到一致对外,方便使用。此时即是在(2)的基础上,做一个统一的编码转换即可,在java中的实现如下
源网页的字节数组为source_byte_array
转换为正常的字符串: String normal_source_str=new String(source_byte_array,C),此时可以用java api直接存储,但往往不直接写入字符串,因为一般的爬虫存储都是多个源网页存储到一个文件中,所以要记录字节偏移量,故下一步。
再将得到的str转换为统一的编码C格式的字节数组,则byte[] new_byte_array=normal_Bytes(C)即可,此时即可用java io api将数组写入文件,并记录相应的字节数组偏移量等,待真正使用时,直接io读取即可。
一,通过http header中的content_type中的charset来获得,该编码是最准确的。
二,通过得到源网页的meta的charset来获得编码。
三,通过智能探测,如cpdetector,它是目前口碑最好的java实 现的智能探测编码,是基于统计实现的,所以注定会有一定的错误率,经过我的实测,若干特殊网页,它确实是不准确的,如网页的meta中charset和实 际的浏览器识别的正常显示的charset不相同的情况,它的识别也是错误的,所以最后我坚决没用它,而用了基于简单规则的方式,实际
测试1000个种子 网址证明,没发现任何乱码,除了一个站点它自身是乱码之外。
重点说下乱码的解决策略:
一、首先读取http header中的content_type的charset,如果有,则认定该charset是肯定准确的,直接做为解码的编码格式即可。
二、再按系统默认编码即UTF-8,去按行读取源网页中的meta和title的值,由于这两个值均为英文标签,所以在获取时肯定不会受到乱码的影响,故可以按UTF-8方式准确获取charset和title的值,此时的title有可能是乱码。
三、由于有不少中文站点中,虽然meta中的charset显示的是utf-8或是GBK,但实际的浏览器解析到的正常编码正好相反为gbk或是UTF-8,面对这种特例,而又发现只有在国内的站点会有如此情况,故做规则如下:
(1)首先判断此时的title若均为标点、字母、数字、中英文符号、GB18030的中文字符等,则认为此次的默认编码就是源网页的实际编码,而不管 获得的charset是怎样的,并将charset设成为系统的默认编码utf-8。
(2)如果title满足第(1)条件,则用得到的charset去解码原始的字节流(如果charset就是utf-8,则省略后一步,直接将该 charset作为实际的编码处理,即utf-8,原因在于很多俄文、西里尔文的标题多是UTF-8编码,但均不属于中文行列)。并获取新解析出来的源网 页字符串的title。此时的新解码的charset即为最终的源网页认定的charset。
解码完成后,在保存源网页的实际数据时,先对得到的原始字节数组按上一步得到的charset解码,即
String source_webpage_string=new String(original_byte_array,charset);
此时得到的source_webpage_string即为正常的源网页中,再进行重编码: new_byte_array=source_Bytes(system.defaultEncoding);// 即utf-8 再用utf-8对正常的串进行编
此时得到的source_webpage_string即为正常的源网页中,再进行重编码:
new_byte_array=source_Bytes(system.defaultEncoding);//即utf-8
简单网页 再用utf-8对正常的串进行编码,得到统一编码下的字节数组,通过java io写入到即定的大文件中即可。
当然如果charset值就是默认的utf-8,则无需解码,直接存储即可。
有人会问为何要先解码?答案是:解码是为了统一编码。做为爬虫系统,会有来自成千上万个站点的网页存储到系统中,而网页的编码有很多,像GBK、 Unicode、big5、shift-js、windows-1521等等,如果直接存储而不统一编码在应用端读取的时候,就要读出字节数组后按原始的 编码解析才能得到非乱码显示,到视图端显示的时候也要如此转化,显然这样做是不合理的,而应该是在爬取下来存储的时候,都统一存成UTF-8编码的即可, 统一之后,无论哪一端来读,都可以直接按UTF-8来处理。这也是统用、主流做法。
经过如上的处理,各个国家的站点出现乱码的概率几乎为0,本人的实际测试情况亦是如此。
希望对正在学习网络爬虫的同学们有帮助。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论