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>

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

None.gifWebRequest webRequest =  WebRequest.Create(url);
None.gifWebResponse webResponse 
=
 webRequest.GetResponse();
None.gif Stream stream 
=
 webResponse.GetResponseStream();
None.gif
None.gif StreamReader sr 
= new
 StreamReader(stream, Encoding.Default);
None.gif 
string html =
 sr.ReadToEnd();
None.gif 
return
 html;
None.gif

常见的识别编码格式都是要么从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检测编码格式的代码:

ContractedBlock.gif ExpandedBlockStart.gif DetectEncoding
None.gifprivate void DetectEncoding()
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif{
InBlock.gif            
if (this.byteLen >= 2)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
this._detectEncoding = false;
InBlock.gif                
bool flag1 = false;
InBlock.gif                
if ((this.byteBuffer[0== 0xfe&& (this.byteBuffer[1== 0xff))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
this.encoding = new UnicodeEncoding(truetrue);
InBlock.gif                    
this.CompressBuffer(2);
InBlock.gif                    flag1 
= true;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else if ((this.byteBuffer[0== 0xff&& (this.byteBuffer[1== 0xfe))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
if (((this.byteLen >= 4&& (this.byteBuffer[2== 0)) && (this.byteBuffer[3== 0))
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        
this.encoding = new UTF32Encoding(falsetrue);
InBlock.gif                        
this.CompressBuffer(4);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        
this.encoding = new UnicodeEncoding(falsetrue);
InBlock.gif                        
this.CompressBuffer(2);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    flag1 
= true;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else if (((this.byteLen >= 3&& (this.byteBuffer[0== 0xef)) && ((this.byteBuffer[1== 0xbb&& (this.byteBuffer[2== 0xbf)))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
this.encoding = Encoding.UTF8;
InBlock.gif                    
this.CompressBuffer(3);
InBlock.gif                    flag1 
= true;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else if ((((this.byteLen >= 4&& (this.byteBuffer[0== 0)) && ((this.byteBuffer[1== 0&& (this.byteBuffer[2== 0xfe))) && (this.byteBuffer[3== 0xff))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
this.encoding = new UTF32Encoding(truetrue);
InBlock.gif                    flag1 
= true;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else if (this.byteLen == 2)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
this._detectEncoding = true;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
if (flag1)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
this.decoder = this.encoding.GetDecoder();
InBlock.gif                    
this._maxCharsPerBuffer = this.encoding.GetMaxCharCount(this.byteBuffer.Length);
InBlock.gif                    
this.charBuffer = new char[this._maxCharsPerBuffer];
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedBlockEnd.gif        }


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值