UDN-企业互联网技术人气社区
UDN 企业互联网技术社区 U视野 再谈网络爬虫中的编码识别问题

再谈网络爬虫中的编码识别问题

开发者头条 开发者头条 2016-4-21 10:41
分享到:
摘要: Nchardet 是通过尝试转换编码比较错误率来实现编码猜测的,由于网页的长度与中文密度的关系,效果并不理想。之前的方法中默认采用了这种方案,导致了较大的CPU资源的开销。下面将介绍新的解析思路: ...

  在之前的文章中,我给大家介绍了 Nchardet 结合网页头部声明来识别网页的编码。通过较长时间段生产环境的使用,效果并不是十分理想。首先是 Nchardet 带来了极大的CPU的开销,尤其是对大规模的爬虫集群来说几乎无法接受;其次猜测的准确性距离100%还有一段距离。因此,就有了今天的这篇文章。

  Nchardet 是通过尝试转换编码比较错误率来实现编码猜测的,由于网页的长度与中文密度的关系,效果并不理想。之前的方法中默认采用了这种方案,导致了较大的CPU资源的开销。下面将介绍新的解析思路:

  首先我们通过解析 HTTP 请求返回的 ContentType 来猜测编码,.Net的网络请求库中的 HttpClient 就是使用了这一方法,实现代码如下:

  1. string Hcharset = "";
  2. string text = "";
  3. if (!string.IsNullOrEmpty(text = httpWebResponse.ContentType))
  4. {
  5.     text = text.ToLower(CultureInfo.InvariantCulture);
  6.     string[] array = text.Split(new char[] { ';', '=', ' ' });
  7.     bool flag = false;
  8.     string[] array2 = array;
  9.     for (int i = 0; i < array2.Length; i++)
  10.     {
  11.         string text2 = array2[i];
  12.         if (text2 == "charset")
  13.             flag = true;
  14.         else
  15.         {
  16.             if (flag)
  17.                 Hcharset = text2;
  18.         }
  19.     }
  20. }
复制代码


  其中 httpWebResponse 为网络请求返回的结果。

  然后我们使用正则来解析用户在网页中声明的编码:

  1. string CharsetReg = @"(meta.*?charset=""?(?<Charset>[^\s""'>;]+)""?)|(xml.*?encoding=""?(?<Charset>[^\s"">;]+)""?)";

  2. string Rcharset = "";
  3. String cache = string.Empty;

  4. while (true)
  5. {
  6.     var b = ResponseStream.ReadByte();
  7.     if (b < 0) //end of stream
  8.         break;
  9.     bytes.Add((byte)b);

  10.     if (!cache.EndsWith("</head>", StringComparison.OrdinalIgnoreCase))
  11.         cache += (char)b;
  12. }

  13. Match match = Regex.Match(cache, CharsetReg, RegexOptions.IgnoreCase | RegexOptions.Multiline);
  14. if (match.Success)
  15.     Rcharset = match.Groups["Charset"].Value;
复制代码

  由于两种方法取到的结果都有不准确的可能,因此当它们相同时我们就采用该种方式解析网页获得内容,避免使用 Nchardet 带来性能损耗。当两者不同时我们才使用 Nchardet 进行猜测,同样与之前的两种结果比较选择相同的方式解析。

  1. if (!string.IsNullOrEmpty(Rcharset) && !string.IsNullOrEmpty(Hcharset) && Hcharset.ToUpper() == Rcharset.ToUpper())
  2.     encode = Encoding.GetEncoding(Hcharset);
  3. else
  4. {
  5.     Ncharset = NChardetHelper.RecogCharset(bytes.ToArray(), Thrinax.Data.NChardetLanguage.CHINESE, 1024);

  6.     if (!string.IsNullOrEmpty(Ncharset) && (Ncharset.ToUpper() == Rcharset.ToUpper() || Ncharset.ToUpper() == Hcharset.ToUpper()))
  7.         encode = Encoding.GetEncoding(Ncharset);
  8. }
复制代码


  如果以上方法还是得不到相同的编码时,我们使用人工标注的编码(如果有的话)。接下来我们使用以下顺序来解析网页,直到获取到使用的编码:

  人工标注的编码 > 网页自动识别 > 解析ContentType > 解析Html编码声明 。

  1. //2,使用人工标注的编码
  2. if (encode == null && !string.IsNullOrEmpty(encoding))
  3. {
  4.     try
  5.     {
  6.         encode = Encoding.GetEncoding(encoding);
  7.     }
  8.     catch { }
  9. }

  10. //3,使用单一方式识别出的编码,网页自动识别 > 解析ContentType > 解析Html编码声明
  11. if (encode == null && !string.IsNullOrEmpty(Ncharset))
  12.     encode = Encoding.GetEncoding(Ncharset);
  13. if(encode == null && !string.IsNullOrEmpty(Hcharset))
  14.     encode = Encoding.GetEncoding(Hcharset);
  15. if (encode == null && !string.IsNullOrEmpty(Rcharset))
  16.     encode = Encoding.GetEncoding(Rcharset);
复制代码


  如果都没有的话,听天由命吧,直接使用默认编码解析。下面是该逻辑和取出内容的部分:

  1. //4,使用默认编码,听天由命吧
  2. if (encode == null)
  3.     encode = Encoding.Default;

  4. Content = encode.GetString(bytes.ToArray());
  5. ResponseStream.Close();
复制代码


  ok,大功告成!所有代码以及里面的一些依赖都已经在 thrinax 库中了,编码识别模块通过长时间生产环境爬虫使用,统计结果显示识别准确率大于 99.99%。相比之前方式的性能提高数倍。thrinax 的定位是一个使用 .Net 提供网络抓取,信息抽取,自然语言处理的库,其中的大部分代码都会来源于社区或其它开源算法或者论文,该库只是筛选统一方便调用。如果大家有这三方面的好的推荐,可以通过 Issue 或者博客评论发给我!


相关阅读

分享到:

最新评论

一周焦点

    关注我们:
    关于我们
    联系我们
    • 电话:010-86393388
    • 邮件:udn@yonyou.com
    • 地址:北京市海淀区北清路68号
    移动客户端下载
    关注我们
    • 微信公众号:yonyouudn
    • 扫描右侧二维码关注我们
    • 专注企业互联网的技术社区
    版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
    返回顶部