HtmlAgilityPack 抓取中文页面乱码问题的解决方案

本文介绍使用HtmlAgilityPack爬取中文网页时出现乱码的问题及解决方案。通过自定义HTTP请求并指定正确的编码方式,成功解决了乱码问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    HtmlAgilityPack是用C#写的开源Html Parser。不过它的某些方面设计不尽完善,比如,按照其正常模式抓取中文网页,往往获得的是乱码。比如,抓取新华网首页( http://xinhua.org)。模仿HtmlAgilityPack示例,爬取代码如下:

None.gif             HtmlWeb hw  =   new  HtmlWeb();
None.gif            
string  url  =   @" http://xinhua.org " ;
None.gif            HtmlDocument doc 
=  hw.Load(url);
None.gif            doc.Save(
" output.html " );

    获得的页面用ie打开,是乱码。

    穿越HtmlAgilityPack的代码迷宫,最后发现问题出在HtmlWeb类的Get(Uri uri, string method, string path, HtmlDocument doc)方法中。该方法有以下代码:

None.gif             HttpWebResponse resp;
None.gif
None.gif            
try
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                resp 
= req.GetResponse() as HttpWebResponse;
ExpandedBlockEnd.gif            }

None.gif            ……
None.gif            
if  ((resp.ContentEncoding  !=   null &&  (resp.ContentEncoding.Length > 0 ))
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                respenc 
= System.Text.Encoding.GetEncoding(resp.ContentEncoding);
ExpandedBlockEnd.gif            }

None.gif            
else
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                respenc 
= null;
ExpandedBlockEnd.gif            }

None.gif            ……
None.gif            Stream s 
=  resp.GetResponseStream();
None.gif            
if  (s  !=   null )
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                
if (UsingCache)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
// NOTE: LastModified does not contain milliseconds, so we remove them to the file
InBlock.gif
                    SaveStream(s, cachePath, RemoveMilliseconds(resp.LastModified), _streamBufferSize);
InBlock.gif
InBlock.gif                    
// save headers
InBlock.gif
                    SaveCacheHeaders(req.RequestUri, resp);
InBlock.gif
InBlock.gif                    
if (path != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        
// copy and touch the file
InBlock.gif
                        IOLibrary.CopyAlways(cachePath, path);
InBlock.gif                        File.SetLastWriteTime(path, File.GetLastWriteTime(cachePath));
ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

InBlock.gif                
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
// try to work in-memory
InBlock.gif
                    if ((doc != null&& (html))
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        
if (respenc != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            doc.Load(s, respenc);
ExpandedSubBlockEnd.gif                        }

InBlock.gif                        
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            doc.Load(s, 
true);
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

InBlock.gif                resp.Close();
ExpandedBlockEnd.gif            }


其中resp是http请求的response。设置断点发现resp.ContentEncoding为空。于是最后的加载行为便变成了doc.Load(s, true);而这个load方法也可能出了问题,最后得到的是乱码。

解决方法:

不使用HttpWeb,该类不成熟。自己写http请求,代码如下:

None.gif             HttpWebRequest req;
None.gif            req 
=  WebRequest.Create( new  Uri( @" http://xinhua.org " ))  as  HttpWebRequest;
None.gif            req.Method 
=   " GET " ;
None.gif            WebResponse rs 
=  req.GetResponse();
None.gif            Stream rss 
=  rs.GetResponseStream();
None.gif            String url 
=   @" http://xinhua.org " ;
None.gif            
try
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                HtmlDocument doc 
= new HtmlDocument();
InBlock.gif                doc.Load(rss);
InBlock.gif                doc.Save(
"output.html");
ExpandedBlockEnd.gif            }

None.gif            
catch  (Exception e)
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                Console.WriteLine(e.Message.ToString());
InBlock.gif                Console.WriteLine(e.StackTrace);
ExpandedBlockEnd.gif            }

None.gif

上面代码中,doc.Load(…) 使用的编码为System.Text.Encoding.Default,在我机器上为gb2312编码。

HtmlDocument也可以指定编码load stream。获得指定编码有两种方法:
(1)在HttpWebResponse 对象中可以获取html代码中设置的charset;
(2)未提供charset的html页面,HtmlDocument提供了自动检测代码的方法DetectEncoding(…)。这一方法俺为测试过,不知道正确性如何.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值