WebClient.DownloadString () restituisce una stringa con caratteri peculiari

Ho un problema con alcuni contenuti che stiamo scaricando dal Web per uno strumento di scraping della schermata che sto creando.

nel codice seguente, la stringa restituita dal metodo di download della stringa del client Web restituisce alcuni caratteri dispari per il download di origine per alcuni (non tutti) siti Web.

Di recente ho aggiunto le intestazioni http come di seguito. Precedentemente lo stesso codice è stato chiamato senza le intestazioni allo stesso effetto. Non ho provato variazioni sull’intestazione ‘Accept-Charset’, non so molto sulla codifica del testo oltre alle basi.

I personaggi, o sequenze di caratteri a cui mi riferisco sono:

ï» ¿

e

Â

Questi caratteri non si vedono quando si utilizza “visualizza origine” in un browser web. Cosa potrebbe causare questo e come posso risolvere il problema?

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); 

 è la rappresentazione di Windows 1252 degli ottetti EF BB BF . Questo è il marcatore di ordine byte UTF-8 , che implica che la pagina Web remota è codificata in UTF-8 ma la stai leggendo come se fosse Windows-1252. Secondo i documenti , WebClient.DownloadString utilizza Webclient.Encoding come codifica quando converte la risorsa remota in una stringa. Impostalo su System.Text.Encoding.UTF8 e le cose dovrebbero teoricamente funzionare.

Il modo in cui WebClient.DownloadString viene implementato è molto stupido. Dovrebbe ottenere la codifica dei caratteri dall’intestazione Content-Type nella risposta, ma invece si aspetta che lo sviluppatore comunichi in anticipo la codifica prevista. Non so cosa stessero pensando gli sviluppatori di questa class.

Ho creato una class ausiliaria che recupera il nome di codifica dall’intestazione Content-Type della risposta:

 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 è una class di eccezioni personalizzata, non esitare a sostituire per InvalidOperationException o qualsiasi altra cosa se vuoi)

Quindi il seguente metodo di estensione per la class WebClient farà il trucco:

 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); } } 

Quindi nel tuo esempio dovresti fare:

 urlData = wc.DownloadStringAwareOfEncoding(uri); 

… e questo è tutto.

 var client = new WebClient { Encoding = System.Text.Encoding.UTF8 }; var json = client.DownloadString(url); 

Nel mio caso i dati restituiti erano compressi con gzip e dovevano essere prima decompressi, quindi ho trovato utile questa risposta:

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

nel mio caso, ho cancellato sempre l’intestazione relativa alla lingua, al set di caratteri ecc. tranne l’agente utente e il cookie. ha funzionato..

  // 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"); 

Nessuno di loro ha funzionato per me per alcuni siti Web speciali come “www.yahoo.com”. L’unico modo per risolvere il mio problema è stato il passaggio da DownloadString a OpenRead e l’utilizzo dell’intestazione UserAgent come codice di esempio. Tuttavia, alcuni siti come “www.varzesh3.com” non hanno funzionato con nessuno dei metodi!

 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();