crawler_网络爬虫中编码的正确处理与乱码的解决策略

 

转载: http://hi.baidu.com/erliang20088/item/9156132bdaeae8949c63d134java

 最近一个月一直在对nutch1.6版进行中等层次的二次开发,原本是想从新作一个自写的爬虫系统,鉴于前基作过微博爬虫系统,感受再重写一个完整的爬虫费时、费力还没太大的含金量,故而直接基于nutch开发。web

    之因此说中是由于没有改动nutch的核心部分map/reduce,但改动了除此以外的绝大部分问题,最终造成了任务提交多样化、调度合理、数据流优化、乱码处理、源码与正文保存等较为合理的网络爬虫系统。通过处理以后,本爬虫能够采集中文、繁体、英文、藏文、俄文、日文、西里尔文等多种文字。现就网络爬虫中常常遇到的乱码问题,给予简要总结。windows

    若是直接用nutch提供的web页面(nutch1.2版及以前是有自带的web展现系统,但以后的版本就没有了)就会很容易发现,老是会出现或多或少的页面。当深刻源码后你会发现,nutch源码中对源网页中的编码的识别是很浅的,统一用utf-8来处理,没有作相关的编码的精准识别,只是留了相应的接口,供二次开发人员自行添加如cpdetector之类的基于统计的编码识别方式。数组

    本系统的二次开发,主要采用截取nutch数据流的开始和结尾两部分数据来作,即提交数据、网页源码的数据两部分来作。网页经过阅读源码的网络IO代码会发现,http网页源码的流的读出是经过socket链接,获得其inputstream输入流以后,以字节流的方来来读的。浏览器

     这里有个重点:若是源网页是GBK字节流,在程序端接收时的inputstream获得的字节数组的编码方式确定是GBK字节流,即源网页是什么编码方式的字节流,程序端接收到的字节流的编码方式确定是相同的。所以,只要在程序端解析出该流实际的编码方式便可将该流得到的源网页的字节数组转化成正常的编码显示形式。即算“解码--解析编码”的过程。网络

     解析字节流的编码主要有三种方式,socket

         一,经过http header中的content_type中的charset来得到,该编码是最准确的。学习

         二,经过获得源网页的meta的charset来得到编码。测试

         三,经过智能探测,如cpdetector,它是目前口碑最好的java实现的智能探测编码,是基于统计实现的,因此注定会有必定的错误率,通过个人实测,若干特殊网页,它确实是不许确的,如网页的meta中charset和实际的浏览器识别的正常显示的charset不相同的状况,它的识别也是错误的,因此最后我坚定没用它,而用了基于简单规则的方式,实际测试1000个种子网址证实,没发现任何乱码,除了一个站点它自身是乱码以外。优化

     重点说下乱码的解决策略:

         1、首先读取http header中的content_type的charset,若是有,则认定该charset是确定准确的,直接作为解码的编码格式便可。  

         2、再按系统默认编码即UTF-8,去按行读取源网页中的meta和title的值,因为这两个值均为英文标签,因此在获取时确定不会受到乱码的影响,故能够按UTF-8方式准确获取charset和title的值,此时的title有多是乱码。

         3、因为有很多中文站点中,虽然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_webpage_string.getBytes(system.defaultEncoding);//即utf-8

          再用utf-8对正常的串进行编码,获得统一编码下的字节数组,经过java io写入到即定的大文件中便可。

          固然若是charset值就是默认的utf-8,则无需解码,直接存储便可。

     有人会问为什么要先解码?答案是:解码是为了统一编码。作为爬虫系统,会有来自成千上万个站点的网页存储到系统中,而网页的编码有不少,像GBK、Unicode、big五、shift-js、windows-1521等等,若是直接存储而不统一编码在应用端读取的时候,就要读出字节数组后按原始的编码解析才能获得非乱码显示,到视图端显示的时候也要如此转化,显然这样作是不合理的,而应该是在爬取下来存储的时候,都统一存成UTF-8编码的便可,统一以后,不管哪一端来读,均可以直接按UTF-8来处理。这也是统用、主流作法。

     通过如上的处理,各个国家的站点出现乱码的几率几乎为0,本人的实际测试状况亦是如此。

     但愿对正在学习网络爬虫的同窗们有帮助。

相关文章
相关标签/搜索