如何高效、准确、自动识别网页编码
天地连站群可以根据用户输入的初始关键词,获取该关键词搜索引擎的搜索结果,然后逐条获取这些相关文章的内容。这样就需要面对无数网页的各种编码。为了解决这个问题,引入了下面的解决办法:
在引入编码自动识别前,我们有两种途径获取网页的编码信息:
其一、通过服务器返回的header里的charset变量获取
其二、通过页面里的meta 信息获取
正常情况下,如果服务器或者页面有提供这两个参数,而且参数是正确的,那我们抓取网页时就不存在编码的问题了。
但是现实总是会难为我们这些程序员,抓取网页时,经常会出现以下几种情况:
1.这两个参数缺失了
2.这两个参数虽然都提供了,但是不一致
3.这两个参数提供了,但是与网页实际的编码不一致
为了尽可能的自动的获取所有网页的编码,所以引入了编码自动识别
我记得在php里面有个mb_detect函数,貌似识别字符串编码,但他的准确率就不好说了,因为编码的自动识别是一个概率事件,只有当被识别的字符串的长度足够大( 比如超过300个字)时,才能比较可靠。
所有的浏览器都支持自动识别网页编码,比如IE ,firefox 等
我使用的正是由mozzila提供的universalchardet模块,据说比IE自带的识别模块准确率高很多
universalchardet项目的地址在:http://www-archive.mozilla.org/projects/intl/chardet.html
目前universalchardet支持python java dotnet等,php不知道支持不
我比较喜欢写C#,因为VS2010 + viemu是我的至爱,所以我使用了C#版;universalchardet的C#移植版本有很多 ,我使用的版本:http://code.google.com/p/nuniversalchardet/
下面是一段使用的例子,相比其他的C#实现有一点点繁琐:
Stream mystream = res.GetResponseStream();
MemoryStream msTemp = new MemoryStream();
int len = 0;
byte[] buff = new byte[512];
while ((len = mystream.Read(buff, 0, 512)) > 0)
{
msTemp.Write(buff, 0, len);
}
res.Close();
if (msTemp.Length > 0)
{
msTemp.Seek(0, SeekOrigin.Begin);
byte[] PageBytes = new byte[msTemp.Length];
msTemp.Read(PageBytes, 0, PageBytes.Length);
msTemp.Seek(0, SeekOrigin.Begin);
int DetLen = 0;
byte[] DetectBuff = new byte[4096];
CharsetListener listener = new CharsetListener();
UniversalDetector Det = new UniversalDetector(null);
while ((DetLen = msTemp.Read(DetectBuff, 0, DetectBuff.Length)) > 0 && !Det.IsDone())
{
Det.HandleData(DetectBuff, 0, DetectBuff.Length);
}
Det.DataEnd();
if (Det.GetDetectedCharset()!=null)
{
CharSetBox.Text = "OK! CharSet=" + Det.GetDetectedCharset();
PageBox.Text = System.Text.Encoding.GetEncoding(Det.GetDetectedCharset()).GetString(PageBytes);
}
}
上面已经可以识别网页的编码了,看起来很简单,不是吗?如果你以前一直被这个问题困扰,而且有幸看到这个帖子,那么,这一类型的问题就彻底解决了,再也不会因为不知道网页编码,抓取了一堆的?????号回来;嗯,生活从此是那么美好。。。。
我当时也是这么想的
上面讲到编码识别是一种概率事件,所以无法保证100%正确识别,因此后来我还是发现了一些因为识别错误导致返回??号的情况,难道这个问题真的没办法完美解决吗?
世间不会有完美的事情,我深信这点。
好在我们只要完善的解决方案就可以了:我们要让程序知道什么时候自动识别错误了,如果错误了,就去读取和使用服务器和网页提供的编码信息。
我开动脑筋想了一下,想出了一个土办法:对于我们中国人来说,存在编码问题的就是中文网页。如果一个中文网页被正确识别了,里面肯定会有中文字符。bingo,我从网上找了出现频度在top N的汉字(比如“的”字),只要网页里包含这个N个汉字之一,则识别成功,否则识别失败。
这样下来,网页编码识别问题基本上可以轻松解决了。
后记:
不知道有没有人对这个感兴趣,有的话,我还想写一篇这个方面的文章,标题也想好了:《网络IO,无处不异步》,这里指的网络IO只的是http request
天地连站群使用了这个编码识别方法,解决了采集领域一个重大的问题,从此我可以把精力从这个问题里抽出来,研究解决其他的问题了。