WebSpider的编码问题(乱码)浅析

这两天看到几篇关于WebSpider的文章。其中关于抓取网页出现的编码格式问题大家都比较感兴趣,以前在参与帮看网的开发时也遇到过。不过那时候忙于ITDB的BBS开发,没有时间去研究。今天看到解决网爬工具爬取页面信息出现乱码的问题 ,刚好最近离职赋闲在家。所以又挑起了我研究学习的兴趣。现在把我的“研究成果”和大家探讨下:
  下面我按照我解决问题的思路来行文
   1,要根本解决编码问题,先要从编码的理论入手。
   2,计算机是一门实践的科学,多动手尝试吧。

一,和编码相关的理论知识:
 
中文编码处理(1) -- 编码与字符集,我摘录几句:
  如果我们读不同编码的文件 到程序内部处理再保存程另一个文件 涉及到三次编码问题
  1 读入文件使用什么编码
  2 程序中使用什么编码
  3 写出文件使用什么编码
  看到这里。可以知道如果自以为先用某种格式把数据从流中读取出来,然后判断,再转换的方式处理编码问题,那么方法本身就错了。结果自然就是不可预期的。当然上面的话并不代表权威。仅仅做为一种分析的参考。

二,http协议和html的规范关于如何得到一个页面的字符编码三种方法:
1.An HTTP "charset" parameter in a "Content-Type" field.
example:
Content-Type: text/html; charset=EUC-JP

2.A META declaration with "http-equiv" set to "Content-Type" and a value set for "charset".
example:
<META http-equiv="Content-Type" content="text/html; charset=EUC-JP">

3.The charset attribute set on an element that designates an external resource.
example:
<A href="
http://www.w3.org/" charset="ISO-8859-1">W3C Web site</A>

  现在先贴一段常见的抓取网页的代码,方便后续的讨论:

WebRequest webRequest = WebRequest.Create(url);
WebResponse webResponse 
=
 webRequest.GetResponse();
 Stream stream 
=
 webResponse.GetResponseStream();

 StreamReader sr 
= new
 StreamReader(stream, Encoding.Default);
 
string html =
 sr.ReadToEnd();
 
return
 html;

常见的识别编码格式都是要么从HttpWebResponse的ContentEncoding和CharacterSet去分析,要么从提取的网页里的分析(二列出的三种方法),现在的问题就出在既然HttpWebResponse的ContentEncoding和CharacterSet并不可靠。而要从流读数据必须指定编码,但现在并不能可靠的确定数据源的正确编码,而尝试用一种编码格式读然后转又会遭遇上叙一所说的问题。这让我想起了我以前写过的“由一道面试题引起的疑问与思考”里关于XML编码格式问题,里面谈到BOM(字节顺序标记)的问题,转其中的几句话:
 W3C定义了三条XML解析器如何正确读取XML文件的编码的规则:
 1,如果文挡有BOM(字节顺序标记,一般来说,如果保存为unicode格式,则包含BOM,ANSI则无),就定义了文件编码
 2,如果没有BOM,就查看XML声明的编码属性
 3,如果上述两个都没有,就假定XML文挡采用UTF-8编码

其实网页也是一种文本格式的东西,其规则也应该类似,我搜索了下,找到更详细的资料:
 1,如果流中是以0xef, 0xbb, 0xbf开头的话,可以确定编码格式utf-8的
 2,如果流中是以0xff,0xfe开头的话,可以确定编码格式是utf-16的

如果仅仅按照上面所列两种情况去判断的,还显然不够严谨,但是到目前为止,我还没找到更详细的关于各种编码的BOM的更多资料。
写到这里,我不得不告诉你,上面的一切探索对于.net来说都是徒劳的,因为.net已经内置了这样的判断方法:
  StreamReader sr = new StreamReader(stream, Encoding.Default,true);
就多加一个true,ms帮你完成BOM的检测。具体的你可以看MSDN的帮助文挡。

我在开篇说到计算机是一门实践的科学,我测试了几个网页都没发现乱码问题。当然这并不表示就完全没有问题,只是一时没找到让它乱码的网页,如果你发现了,请你一定要告诉我。我们一起来研究下。

最后,我想推翻我刚才的结论:上面的一切探索对于.net来说都是徒劳的;因为我看到下面的代码的时候,我知道why,而不仅仅是how !
Reflector出来的StreamReader关于通过BOM检测编码格式的代码:

DetectEncoding


水平有限,不妥之处,欢迎指正。
 

posted @ 2007-01-22 17:22 kwklover 阅读(2701) 评论(17)  编辑 收藏 网摘 所属分类: 技术总结

  回复  引用  查看    
#1楼2007-01-22 17:44 | anikin      
我用了
StreamReader sr = new StreamReader(stream, Encoding.Default,true);

好像还是乱码

  回复  引用  查看    
#2楼[楼主]2007-01-22 17:53 | kwklover      
@anikin
发一下出现乱码的网址来看看。一起研究下。

  回复  引用  查看    
#4楼[楼主]2007-01-22 19:25 | kwklover      
@anikin
我刚测试了下,抓cnblogs.com确实有乱码问题。不过同时有发现了.net的奇怪的问题:
StreamReader sr = new StreamReader(stream, Encoding.GetEncoding("utf-8"));
来读cnblogs.com,完全没问题;
但用
StreamReader sr = new StreamReader(stream, Encoding.UTF8);
却是有问题。
Encoding.GetEncoding("utf-8")和Encoding.UTF8
难到不相同吗?

Encoding.UTF8.Equals(Encoding.GetEncoding("utf-8"))是true
如何解释?
看来MS的DetectEncoding()并不可靠,但上面的思路是否正确呢?
还要研究下

很抱歉,没测试完整就发出来。
欢迎继续交流。


  回复  引用    
#5楼2007-01-23 08:49 | 雨恨云愁[未注册用户]
我来告诉你吧
Encoding.Default
指的是操作系统的编码
而你测试的那几篇刚好都是GB2312或者GBK的
所以刚好没乱码
不知道你是不是我们群里的人

  回复  引用    
#6楼2007-01-23 08:52 | 雨恨云愁[未注册用户]
你一开始的那句话说的好:
要根本解决编码问题,先要从编码的理论入手
至于判断META,CHARSET都是不可靠的
因为网站制作的门槛变低了
很多人都不遵守你标准
你标准有什么用?
我看你还没遇到更变态的
一个网站混用两种编码 =。=

  回复  引用    
#7楼2007-01-23 08:55 | 雨恨云愁[未注册用户]
应该是了
随风 呵呵

  回复  引用  查看    
#8楼2007-01-23 11:22 | blockhead      
我觉得网页抓取的编码的因为最好是分2遍请求实现,第一步请求的时候不获取网页的具体内容,而是获取Header,从Header中得到CharSet编码,然后再根据编码请求数据
  回复  引用  查看    
#9楼2007-01-23 16:58 | Tiger!      
好像很复杂的样子,我也希望能学到一个好的方法
  回复  引用  查看    
#10楼[楼主]2007-01-23 20:06 | kwklover      
@上面各位朋友

其实我这个判断只解决一个问题,有BOM的情况,但如果是没有BOM呢?那只能从Header的Content-type里取charset,如果连header里没取到,那只能从页面里取

另外一种情况,如果有BOM的化。MS的DetectEncoding也可靠,因为我测试发现Encoding.GetEncoding("utf-8")和Encoding.UTF8取数据获得结果是不同。只有自己实现:
1,实现DetectEncoding检测是否有BOM
2,检查Header是否charset
3, 检查页面是否有charset
4,如果上面都没有,一种方法是设置默认为utf-8,或者一个byte,一个byte做字符范围分析,然后取最大哪个字符集。但是不敢肯定这种实现是否可以,在研究中。

  回复  引用    
#11楼2007-04-15 22:07 | kevin.c[未注册用户]
似乎还是没有完美的解决方案啊,呵呵
  回复  引用    
#12楼2007-05-08 23:27 | 猴有一个梦想[未注册用户]
第二种情况 检查页面是否有charset
得到了,怎么转换呢?

  回复  引用    
#13楼2007-05-08 23:30 | 猴有一个梦想[未注册用户]
"可以知道如果自以为先用某种格式把数据从流中读取出来,然后判断,再转换的方式处理编码问题,那么方法本身就错了。结果自然就是不可预期的"
真理啊,我就自作聪明了,
那从页面中提取charset后怎么转换啊

  回复  引用    
#14楼2007-05-23 22:15 | 电子网[未注册用户]
http://www.example.net.cn/google-ranking-factors.html
这个网站就是典型的写着gb2312的utf-8编码的网页

  回复  引用    
#15楼2007-05-24 21:24 | xnp[未注册用户]
上面的方法都试过,有些乱码站总是乱码。无法正确读取
  回复  引用    
#16楼2007-05-24 21:31 | xnp[未注册用户]

你试试获取这个地址,读出来的贴子内容始终是乱码

http://www.9k8.com/DotWe_BBS.aspx?A=D3_1&B1_ID=717&B1_TB=0

  回复  引用    
#17楼2008-01-24 22:16 | Hikey[未注册用户]
呵呵,告诉你一个秘密
Reflector可以找到另一个DetectEncoding()

那个就是你要找的东西




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 627173




相关文章:

相关链接: