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

时间:2024-06-04 23:06:38

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

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

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

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

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

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

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

一,通过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_webpage_string.getBytes(system.defaultEncoding);//即utf-8

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

当然如果charset值就是默认的utf-8,则无需解码,直接存储即可。

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

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

希望对正在学习网络爬虫的同学们有帮助。