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

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

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

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

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

            HttpWebResponse resp;

            
try
            
{
                resp 
= req.GetResponse() as HttpWebResponse;
            }

            ……
            
if ((resp.ContentEncoding != null&& (resp.ContentEncoding.Length>0))
            
{
                respenc 
= System.Text.Encoding.GetEncoding(resp.ContentEncoding);
            }

            
else
            
{
                respenc 
= null;
            }

            ……
            Stream s 
= resp.GetResponseStream();
            
if (s != null)
            
{
                
if (UsingCache)
                
{
                    
// NOTE: LastModified does not contain milliseconds, so we remove them to the file
                    SaveStream(s, cachePath, RemoveMilliseconds(resp.LastModified), _streamBufferSize);

                    
// save headers
                    SaveCacheHeaders(req.RequestUri, resp);

                    
if (path != null)
                    
{
                        
// copy and touch the file
                        IOLibrary.CopyAlways(cachePath, path);
                        File.SetLastWriteTime(path, File.GetLastWriteTime(cachePath));
                    }

                }

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

                        
else
                        
{
                            doc.Load(s, 
true);
                        }

                    }

                }

                resp.Close();
            }


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

解决方法:

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

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

            
catch (Exception e)
            
{
                Console.WriteLine(e.Message.ToString());
                Console.WriteLine(e.StackTrace);
            }

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

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

posted @ 2007-06-24 23:53 xiaotie 阅读(3018) 评论(9)  编辑 收藏 网摘 所属分类: [项目]Web风行者

  回复  引用    
#1楼2007-06-25 00:18 | 小鬼[未注册用户]
using (Stream outStream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(outStream, Encoding.GetEncoding("gb2312")))
{
outdata = sr.ReadToEnd();
}
}
我倒喜欢这样搞搞。

  回复  引用  查看    
#2楼[楼主]2007-06-25 01:09 | xiaotie      
@小鬼
HtmlDocument 已经把html代码解析成节点树,把它储存为文件只是想看看它解析结果的正确性如何。

  回复  引用  查看    
#3楼2007-06-25 01:14 | 问天      
严格的说,只是因为对方web server没有在http header中明确给出自己网页编码信息造成的……

使用System.Text.Encoding.Default这样的做法并不能称得上是解决乱码……这样处理,遇上utf-8编码未给header的中文页面照样乱码……

偶处理的时候是使用ISO 8859-1去作为默认编码……网页读下来后,再去寻找其中类似:<meta http-equiv="Content-Type" content="text/html; charset=gb2312">的字符串,然后再做编码转换处理……

ISO 8859-1可以保证网页内容不会因为转换而丢失……

  回复  引用  查看    
#4楼[楼主]2007-06-25 06:31 | xiaotie      
@问天
<meta http-equiv="Content-Type" content="text/html; charset=gb2312"> 这个 HttpWebResponse
已经自己处理了。HtmlAgilityPack也提供了自动检测代码的方法。
在具体应用中,可与先看 HttpWebResponse有没有提供CharSet。如果没有提供,则使用HtmlAgilityPack中HtmlDocument对象的DetectEncoding(...)方法。
见讨论。

  回复  引用    
#5楼2007-06-26 15:23 | A.Z[未注册用户]
经过我昨天和今天无比的痛苦的忍受这个 HtmlAgilityPack ,终于决定抛弃它了。
它的html parser写的很烂,烂也就算了,关键在于输出是错误的。我愤怒了。
不过他桥接XPathNavigator的类HtmlNodeNavigator倒是蛮有创意的,可以改造一下,和我新发现的PVax(Alexey A. Popov可能是个俄罗斯人)合作。

  回复  引用    
#6楼2007-06-27 18:17 | 安安[未注册用户]
在 HtmlWeb.cs 第424行改写如下:
原来:
if ((resp.ContentEncoding != null) && (resp.ContentEncoding.Length>0))
{
respenc = System.Text.Encoding.GetEncoding(resp.ContentEncoding);
}
else
{
respenc = null;//这里加扩展代码
}


改写如下:
if ((resp.ContentEncoding != null) && (resp.ContentEncoding.Length>0))
{
respenc = System.Text.Encoding.GetEncoding(resp.ContentEncoding);
}
else
{
respenc = null;
if (html)
{
string charset = NameValuePairList.GetNameValuePairsValue(resp.ContentType, "charset");
if (charset != null)
{
respenc = System.Text.Encoding.GetEncoding(charset);
}
}
}

  回复  引用    
#7楼2007-07-05 17:44 | SKII[未注册用户]
请教一下,为什么以下两段代码输出结果完全不一样

[STAThread]
static void Main(string[] args)
{
//HtmlDocument doc = new HtmlDocument();
//doc.Load(@"..\..\mshome.htm");
HtmlWeb hw = new HtmlWeb();
string url = @"http://www.google.cn";
HtmlDocument doc = hw.Load(url);
doc.OptionOutputAsXml = true;
doc.Save("mshome.xml");


MemoryStream ms = new MemoryStream();
doc.Save(ms,System.Text.Encoding.UTF8);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string a = sr.ReadToEnd();
Console.Write(a);
Console.ReadLine();
}

存在mshome里的一切正常
但是通过控制台输出的则少了很多很多,到底是怎么回事情呢

  回复  引用    
#9楼2008-11-22 16:55 | 雪影银狐[未注册用户]
其实很简单:

if ((resp.ContentEncoding != null) && (resp.ContentEncoding.Length>0))
{
respenc = System.Text.Encoding.GetEncoding(resp.ContentEncoding);
}
else
{
respenc = null;//这里加扩展代码
}

改成:

if ((resp.ContentEncoding != null) && (resp.ContentEncoding.Length>0))
{
respenc = System.Text.Encoding.GetEncoding(resp.ContentEncoding);
}
else
{
respenc = System.Text.Encoding.GetEncoding("GB2312");
}




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

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

0 794240




相关文章:

相关链接: