2011-01-17 2 views
52

У меня проблема с некоторым контентом, который мы загружаем из Интернета, для инструмента для очистки экрана, который я создаю.WebClient.DownloadString() возвращает строку со своеобразными символами

в приведенном ниже коде, строка, возвращаемая методом загрузки строки веб-клиента, возвращает некоторые нечетные символы для загрузки исходного кода для нескольких (не всех) веб-сайтов.

Я недавно добавил http заголовки, как показано ниже. Ранее этот же код вызывался без заголовков с тем же эффектом. Я не пробовал варианты в заголовке «Accept-Charset», я не очень разбираюсь в текстовой кодировке, отличной от основ.

Характеры, или последовательности символов, которые я имею в виду, являются:

"ï» ¿ "

и

" Â "

Эти символы не видны когда вы используете «источник просмотра» в веб-браузере. Что может быть причиной этого и как я могу исправить проблему?

string urlData = String.Empty; 
WebClient wc = new WebClient(); 

// Add headers to impersonate a web browser. Some web sites 
// will not respond correctly without these headers 
wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12"); 
wc.Headers.Add("Accept", "*/*"); 
wc.Headers.Add("Accept-Language", "en-gb,en;q=0.5"); 
wc.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); 

urlData = wc.DownloadString(uri); 
+0

Я получаю ту же ошибку с приложением Windows, которое я написал, чтобы минимизировать мои JS и CSS с помощью C# YUI Compessor. Он выдает ошибки в файлах, которые возвращаются с точными символами, указанными выше. Я указываю 'client.Encoding = Encoding.UTF8;' и он все равно возвращает фанковые символы ... Также я пытаюсь выяснить, как обрабатывать ошибки, создаваемые C# YUI Compressor, такие как [ERROR] Invalid Syntax ... – pixelbobby

+0

Прошло некоторое время с тех пор, как я впервые столкнулся с этой проблемой и с тех пор узнал о кодировке текста. Чтобы помочь вам, в основном то, что вам нужно сделать, это попробовать и сопоставить кодировку из заголовков http с ответом. Оттуда декодируют поток байтов, используя обнаруженную кодировку. Если кодировка не включена в заголовки, декодируется с UTF8, а затем искать кодировку в документе HTML. Если в документе HTML все еще нет, вы остаетесь только с эвристикой. Я читал о различных механизмах, но здесь нет простого решения. – gb2d

+0

Я отправлю код обратно в следующий раз, когда я получу шанс. – gb2d

ответ

87

 является Windows-1252 представление октетов EF BB BF. Это the UTF-8 byte-order marker, что означает, что ваша удаленная веб-страница закодирована в UTF-8, но вы читаете ее, как если бы это были окна-1252. According to the docs, WebClient.DownloadString использует Webclient.Encoding как свою кодировку, когда преобразует удаленный ресурс в строку. Установите его на System.Text.Encoding.UTF8, и все должно теоретически работать.

+1

Спасибо, хотя это создает проблемы на других сайтах. Теперь я вижу алмаз с вопросительным знаком. Я предполагаю, что я указываю кодировку в заголовке http, поэтому я должен ожидать такую ​​же обратную связь с веб-сервером? – gb2d

+1

Независимо от того, что вы указали в заголовке, веб-серверы могут игнорировать и возвращать что-либо. Вы должны быть готовы обращаться к UTF-8 и получать кодировки Windows. –

+2

Если вы не знаете, в какую кодировку будут возвращаться данные, вы можете безопасно играть и получать необработанные байты с помощью ['WebClient.DownloadData'] (http://msdn.microsoft.com/en-us/ библиотека/ms144188.aspx). – dkarp

38

Способ реализации WebClient.DownloadString очень тупой. Он должен получить кодировку символов из заголовка Content-Type в ответе, но вместо этого он ожидает, что разработчик заранее сообщит ожидаемую кодировку. Я не знаю, о чем думали разработчики этого класса.

Я создал вспомогательный класс, который извлекает имя кодировки из Content-Type заголовка ответа:

public static class WebUtils 
{ 
    public static Encoding GetEncodingFrom(
     NameValueCollection responseHeaders, 
     Encoding defaultEncoding = null) 
    { 
     if(responseHeaders == null) 
      throw new ArgumentNullException("responseHeaders"); 

     //Note that key lookup is case-insensitive 
     var contentType = responseHeaders["Content-Type"]; 
     if(contentType == null) 
      return defaultEncoding; 

     var contentTypeParts = contentType.Split(';'); 
     if(contentTypeParts.Length <= 1) 
      return defaultEncoding; 

     var charsetPart = 
      contentTypeParts.Skip(1).FirstOrDefault(
       p => p.TrimStart().StartsWith("charset", StringComparison.InvariantCultureIgnoreCase)); 
     if(charsetPart == null) 
      return defaultEncoding; 

     var charsetPartParts = charsetPart.Split('='); 
     if(charsetPartParts.Length != 2) 
      return defaultEncoding; 

     var charsetName = charsetPartParts[1].Trim(); 
     if(charsetName == "") 
      return defaultEncoding; 

     try 
     { 
      return Encoding.GetEncoding(charsetName); 
     } 
     catch(ArgumentException ex) 
     { 
      throw new UnknownEncodingException(
       charsetName, 
       "The server returned data in an unknown encoding: " + charsetName, 
       ex); 
     } 
    } 
} 

(UnknownEncodingException является пользовательским класс исключений, не стесняйтесь, чтобы заменить на InvalidOperationException или что-то еще, если вы хотите)

Тогда следующий метод расширения для WebClient класса будет делать трюк:

public static class WebClientExtensions 
{ 
    public static string DownloadStringAwareOfEncoding(this WebClient webClient, Uri uri) 
    { 
     var rawData = webClient.DownloadData(uri); 
     var encoding = WebUtils.GetEncodingFrom(webClient.ResponseHeaders, Encoding.UTF8); 
     return encoding.GetString(rawData); 
    } 
} 

Таким образом, в вашем примере вы могли бы сделать:

urlData = wc.DownloadStringAwareOfEncoding(uri); 

... и это все.

+2

После 4 лет такой хороший ответ? Человек, просто из-за этого ты заслуживаешь мой голос, хорошие усилия. – Yaroslav

+0

Я верю, что это неправда. DownloadString использует кодировку из заголовка Content-Type HTTP, проверьте источник: http://referencesource.microsoft.com/#System/net/System/Net/webclient.cs,fd125940dd542ee8, ссылки –

+2

Согласно источнику, 'DownloadString' пытается получить кодировку символов с использованием заголовка Content-Type из запроса, а не ответа. Вот почему расширение Konamiman отлично работает, а 'DownloadString' не – holdenmcgrohen

1

В моем случае данные возвращенного были архивированным и должны были быть распакованы первым, так что я нашел этот ответ полезный:

https://stackoverflow.com/a/34418228/74585

9
var client = new WebClient { Encoding = System.Text.Encoding.UTF8 }; 

var json = client.DownloadString(url); 
+0

это идеальный код –

0

в моем случае, я удалил когда Header, связанные с языком, charset и т. д. Исключить пользовательский агент и cookie. он работал ..

// try commenting 
//wc.Headers.Add("Accept-Language", "en-gb,en;q=0.5"); 
//wc.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); 
0

Ни один из них не работал для меня на некоторых специальных сайтах, таких как «www.yahoo.com». Единственный способ решить мою проблему - сменить DownloadString на OpenRead и использовать заголовок UserAgent, как пример кода. Однако некоторые сайты, такие как «www.varzesh3.com», не работали ни с одним из методов!

WebClient client = new WebClient()  
client.Headers.Add(HttpRequestHeader.UserAgent, ""); 
var stream = client.OpenRead("http://www.yahoo.com"); 
StreamReader sr = new StreamReader(stream); 
s = sr.ReadToEnd(); 
Смежные вопросы