Проблема с кодировкой HttpWebResponse
Вот фрагмент кода:
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(request.RawUrl);
WebRequest.DefaultWebProxy = null;//Ensure that we will not loop by going again in the proxy
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
string charSet = response.CharacterSet;
Encoding encoding;
if (String.IsNullOrEmpty(charSet))
encoding = Encoding.Default;
else
encoding = Encoding.GetEncoding(charSet);
StreamReader resStream = new StreamReader(response.GetResponseStream(), encoding);
return resStream.ReadToEnd();
Проблема заключается в том, что я провожу тестирование по адресу: http://www.google.fr/
Все "é" не отображаются хорошо. Я пытался изменить ASCII на UTF8, и он по-прежнему отображается неправильно. Я протестировал html-файл в браузере, и браузер хорошо отображает html-текст, поэтому я уверен, что проблема заключается в методе, который я использую для загрузки html-файла.
Что я должен изменить?
удалена мертвая ссылка ImageShack
Обновление 1: код и тестовый файл изменены
7 ответов
Во-первых, более простой способ написания этого кода - использовать StreamReader и ReadToEnd:
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(myURL);
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
using (Stream resStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(resStream, Encoding.???);
return reader.ReadToEnd();
}
}
Тогда это просто вопрос поиска правильной кодировки. Как вы создали файл? Если это с Блокнотом, то вы, вероятно, хотите Encoding.Default
- но это явно не переносимо, так как это кодировка по умолчанию для вашего ПК.
В хорошо работающем веб-сервере ответ будет указывать кодировку в его заголовках. Сказав это, заголовки ответа иногда требуют одного, а HTML - другого, в некоторых случаях.
По умолчанию для CharacterSet используется "ISO-8859-1", если он не указан в заголовке типа содержимого сервера (отличается от метатега "charset" в HTML). Я сравниваю HttpWebResponse.CharacterSet с атрибутом charset HTML. Если они разные - я использую кодировку, как указано в HTML, чтобы перечитать страницу еще раз, но на этот раз с правильной кодировкой.
Смотрите код:
string strWebPage = "";
// create request
System.Net.WebRequest objRequest = System.Net.HttpWebRequest.Create(sURL);
// get response
System.Net.HttpWebResponse objResponse;
objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();
// get correct charset and encoding from the server's header
string Charset = objResponse.CharacterSet;
Encoding encoding = Encoding.GetEncoding(Charset);
// read response
using (StreamReader sr =
new StreamReader(objResponse.GetResponseStream(), encoding))
{
strWebPage = sr.ReadToEnd();
// Close and clean up the StreamReader
sr.Close();
}
// Check real charset meta-tag in HTML
int CharsetStart = strWebPage.IndexOf("charset=");
if (CharsetStart > 0)
{
CharsetStart += 8;
int CharsetEnd = strWebPage.IndexOfAny(new[] { ' ', '\"', ';' }, CharsetStart);
string RealCharset =
strWebPage.Substring(CharsetStart, CharsetEnd - CharsetStart);
// real charset meta-tag in HTML differs from supplied server header???
if(RealCharset!=Charset)
{
// get correct encoding
Encoding CorrectEncoding = Encoding.GetEncoding(RealCharset);
// read the web page again, but with correct encoding this time
// create request
System.Net.WebRequest objRequest2 = System.Net.HttpWebRequest.Create(sURL);
// get response
System.Net.HttpWebResponse objResponse2;
objResponse2 = (System.Net.HttpWebResponse)objRequest2.GetResponse();
// read response
using (StreamReader sr =
new StreamReader(objResponse2.GetResponseStream(), CorrectEncoding))
{
strWebPage = sr.ReadToEnd();
// Close and clean up the StreamReader
sr.Close();
}
}
}
Если вы не хотите загружать страницу дважды, я немного изменил код Алекса, используя Как разместить WebResponse в потоке памяти?, Вот результат
public static string DownloadString(string address)
{
string strWebPage = "";
// create request
System.Net.WebRequest objRequest = System.Net.HttpWebRequest.Create(address);
// get response
System.Net.HttpWebResponse objResponse;
objResponse = (System.Net.HttpWebResponse)objRequest.GetResponse();
// get correct charset and encoding from the server's header
string Charset = objResponse.CharacterSet;
Encoding encoding = Encoding.GetEncoding(Charset);
// read response into memory stream
MemoryStream memoryStream;
using (Stream responseStream = objResponse.GetResponseStream())
{
memoryStream = new MemoryStream();
byte[] buffer = new byte[1024];
int byteCount;
do
{
byteCount = responseStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, byteCount);
} while (byteCount > 0);
}
// set stream position to beginning
memoryStream.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(memoryStream, encoding);
strWebPage = sr.ReadToEnd();
// Check real charset meta-tag in HTML
int CharsetStart = strWebPage.IndexOf("charset=");
if (CharsetStart > 0)
{
CharsetStart += 8;
int CharsetEnd = strWebPage.IndexOfAny(new[] { ' ', '\"', ';' }, CharsetStart);
string RealCharset =
strWebPage.Substring(CharsetStart, CharsetEnd - CharsetStart);
// real charset meta-tag in HTML differs from supplied server header???
if (RealCharset != Charset)
{
// get correct encoding
Encoding CorrectEncoding = Encoding.GetEncoding(RealCharset);
// reset stream position to beginning
memoryStream.Seek(0, SeekOrigin.Begin);
// reread response stream with the correct encoding
StreamReader sr2 = new StreamReader(memoryStream, CorrectEncoding);
strWebPage = sr2.ReadToEnd();
// Close and clean up the StreamReader
sr2.Close();
}
}
// dispose the first stream reader object
sr.Close();
return strWebPage;
}
Здесь есть несколько хороших решений, но все они, похоже, пытаются разобрать кодировку из строки типа контента. Вот решение с использованием System.Net.Mime.ContentType, которое должно быть более надежным и более коротким.
var client = new System.Net.WebClient();
var data = client.DownloadData(url);
var encoding = System.Text.Encoding.Default;
var contentType = new System.Net.Mime.ContentType(client.ResponseHeaders[HttpResponseHeader.ContentType]);
if (!String.IsNullOrEmpty(contentType.CharSet))
{
encoding = System.Text.Encoding.GetEncoding(contentType.CharSet);
}
string result = encoding.GetString(data);
Это код, который скачивают один раз.
String FinalResult = "";
HttpWebRequest Request = (HttpWebRequest)System.Net.WebRequest.Create( URL );
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
Stream ResponseStream = Response.GetResponseStream();
StreamReader Reader = new StreamReader( ResponseStream );
bool NeedEncodingCheck = true;
while( true )
{
string NewLine = Reader.ReadLine(); // it may not working for zipped HTML.
if( NewLine == null )
{
break;
}
FinalResult += NewLine;
FinalResult += Environment.NewLine;
if( NeedEncodingCheck )
{
int Start = NewLine.IndexOf( "charset=" );
if( Start > 0 )
{
Start += "charset=\"".Length;
int End = NewLine.IndexOfAny( new[] { ' ', '\"', ';' }, Start );
Reader = new StreamReader( ResponseStream, Encoding.GetEncoding(
NewLine.Substring( Start, End - Start ) ) ); // Replace Reader with new encoding.
NeedEncodingCheck = false;
}
}
}
Reader.Close();
Response.Close();
Все еще возникают проблемы при запросе веб-страницы "www.google.fr" из веб-запроса.
Я проверил необработанный запрос и ответ с Fiddler. Проблема исходит от серверов Google. Заголовки HTTP ответа имеют значение charset=ISO-8859-1, сам текст кодируется с помощью ISO-8859-1, а HTML-код содержит charset=UTF-8. Это бессвязно и приводит к ошибкам кодирования.
После многих испытаний мне удалось найти обходной путь. Просто добавь:
myHttpWebRequest.UserAgent = "Mozilla/5.0";
к вашему коду, и Google Response волшебным образом и полностью превратится в UTF-8.
Я изучил ту же проблему с помощью WireShark, отличного анализатора протоколов. Я думаю, что у класса httpWebResponse есть некоторые недостатки в дизайне. Фактически, вся сущность сообщения была загружена при первом вызове метода GetResponse() класса HttpWebRequest, но в инфраструктуре нет места для хранения данных в классе HttpWebResponse или где-то еще, в результате чего вы должны получить поток ответов. второй раз.