漫谈网页编码识别技术


来源:http://blog.hysia.com/post/6/how-to-detect-webpage-charset/

 

最近处理了很多有关网页编码的问题,积累了一点经验,写出来总结分享下。

我们在用程序处理网页编码的时候总是会要进行一些decodeencode操作,比如把GBK,UTF-8,GB2312等编码互相转换,或者程序内部统一编码成unicode进行各种操作。而这个过程中第一步就是要识别出目标网页使用的是什么编码,然后才能进行各种转换操作。关键是如何准确识别出目标网页的编码呢? 这里我认为有以下几种方法:(感谢知道创宇数据中心同学提供各种样例)
1.HTTP HEADER
这个例如下图所示:

 

通过这个可以作为识别目标网页的编码特征之一。但是,(请注意,我这里用了但是)通常还有如下问题:

  • 响应头中的 Content-Type 没有 charset 特征
  • WebServer Content-Type 返回的一个不规范的 charset,比如我通过检测大量网页捕获到的有:zh-ch.gb2312.gbknozh_cn.gbk0ffon

这个时候 HTTP HEADER中的charset特征就没有了或者说只能作为参考(后面会介绍为什么能作为参考)

  • 2.HTML META CHARSET

还是看图说话:

这个就不用介绍了,搞开发的都知道。但是这个特征也只是能作为参考,千万不要以为用正则匹配出这个meta中的charset就是目标网页的编码了,因为,有很多粗心的程序猿敲字有点快,经常敲错代码。这里同样收集了样例,有图有真相!

 

如果你仅仅通过正则匹配出了meta charset 然后进行 decode,encode 操作,注定程序要报错,要悲剧。那么就没有其他办法来识别目标网页编码了么?答案是必须有啊

  • Heuristic Detect (启发式探测)

故名思议,就是主动探测目标编码。我以python为例,python有个很好的模块叫chardet,可以主动识别目标string的编码,同学们可以通过

sudo easy_install chardet 

或者

sudo pip install chardet 

来安装这个模块.这个模块的具体使用不是我今天介绍的重点,同学们自己查手册或看 doc string. 有这个模块以后呢,通过综合 HTTP HEADER,META CHARSET 就能够准确识别目标网页的编码了。 在这个综合判别算法的过程中要修正 HTTP HEADER,META CHARSET中的错误,手误,不标准的编码,同时还要做一些转换编码别名的操作。python的标准编码参见:http://docs.python.org/library/codecs.html#standard-encodings

这里我贴2段python处理转换编码别名为标准编码的代码片段

#http://docs.python.org/library/codecs.html#standard-encodings translate = { 'windows-874': 'iso-8859-11', 'en_us': 'utf8', 'macintosh': 'iso-8859-1',\ 'euc_tw': 'big5_tw', 'th': 'tis-620','zh-cn': 'gbk','gb_2312-80':'gb2312',\ 'iso-latin-1':'iso-8859-1','windows-31j':'shift_jis','x-sjis':'shift_jis',\ 'none': 'null','no':'null','0ff':'null'} 

还有

# 调整为正确的编码方式 if encoding.startswith('8859'): encoding = 'iso-%s' % encoding elif encoding.startswith('cp-'): encoding = 'cp%s' % encoding[3:] elif encoding.startswith('euc-'): encoding = 'euc_%s' % encoding[4:] elif encoding.startswith('windows') and not encoding.startswith('windows-'): encoding = 'windows-%s' % encoding[7:] elif encoding.find('iso-88') > 0: encoding = encoding[encoding.find('iso-88'):] elif encoding.startswith('is0-'): encoding = 'iso%s' % encoding[4:] elif encoding.find('ascii') > 0: encoding = 'ascii' 

通过综合上面介绍的3种方法,再通过一个类似与非门电路的一个判别算法基本上就能准确识别出一个目标网页的编码了。这里我为什么说是一个类似与非门的算法呢?因为我碰到几例BT的编码:HEADER中一个编码,META 中另外一个编码,而真正的网页编码是还是另外一个!

目前这个网页编码识别模块已经在我们数据中心得到了验证。而识别出网页的编码就能给后续的各种操作减少工作量,谁用谁知道。

在写这个文章个过程中,所有样例都是由知道创宇数据中心提供,我们专注于互联网,是为了更好更安全的互联网。