web-dev-qa-db-fra.com

WebClient.DownloadString () renvoie une chaîne avec des caractères particuliers

J'ai un problème avec certains contenus que nous téléchargeons à partir du Web pour un outil de grattage d'écran que je suis en train de créer.

dans le code ci-dessous, la chaîne renvoyée par la méthode de chaîne de téléchargement du client Web renvoie des caractères impairs pour le téléchargement source pour quelques sites Web (pas tous).

J'ai récemment ajouté des en-têtes http comme ci-dessous. Auparavant, le même code était appelé sans les en-têtes au même effet. Je n'ai pas essayé de variations sur l'en-tête "Accept-Charset", je ne connais pas grand-chose au codage de texte autre que les bases.

Les personnages ou séquences de caractères auxquels je me réfère sont:

"ï" ¿ "

et

"Â"

Ces caractères ne sont pas visibles lorsque vous utilisez "voir la source" dans un navigateur Web. Quelle pourrait en être la cause et comment puis-je corriger le problème?

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

 est la représentation windows-1252 des octets EF BB BF. C'est le marqueur d'ordre des octets UTF-8 , ce qui implique que votre page Web distante est encodée en UTF-8 mais vous la lisez comme si c'était Windows-1252. Selon les documents , WebClient.DownloadString les usages Webclient.Encoding comme son encodage lors de la conversion de la ressource distante en chaîne. Réglez-le sur System.Text.Encoding.UTF8 et les choses devraient théoriquement fonctionner.

99
dkarp

Le chemin WebClient.DownloadString est implémenté est très stupide. Il devrait obtenir le codage des caractères à partir du Content-Type en-tête dans la réponse, mais à la place, il s'attend à ce que le développeur indique au préalable l'encodage attendu. Je ne sais pas ce que les développeurs de cette classe pensaient.

J'ai créé une classe auxiliaire qui récupère le nom d'encodage du Content-Type en-tête de la réponse:

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 est une classe d'exception personnalisée, n'hésitez pas à remplacer pour InvalidOperationException ou autre chose si vous le souhaitez)

Ensuite, la méthode d'extension suivante pour la classe WebClient fera l'affaire:

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

Donc, dans votre exemple, vous feriez:

urlData = wc.DownloadStringAwareOfEncoding(uri);

...et c'est tout.

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

var json = client.DownloadString(url);
12
Sanket Patel

Dans mon cas, les données retournées ont été compressées et ont dû être décompressées en premier, donc j'ai trouvé cette réponse utile:

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

1
Matthew Lock

dans mon cas, j'ai supprimé tous les en-têtes liés à la langue, au jeu de caractères, etc. SAUF agent utilisateur et cookie. ça a marché..

 // 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
bh_earth0

Aucun d'eux ne fonctionnait pour moi pour certains sites Web spéciaux tels que "www.yahoo.com". La seule façon de résoudre mon problème était de remplacer DownloadString par OpenRead et d'utiliser l'en-tête UserAgent comme exemple de code. Cependant, quelques sites comme "www.varzesh3.com" ne fonctionnaient avec aucune des méthodes!

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();
0
Siamak Ferdos