2010-04-23 7 views
9

En utilisant le code suivant, je peux télécharger le code HTML d'un fichier à partir d'Internet:caractères dans la chaîne a changé après avoir téléchargé HTML à partir de l'Internet

WebClient wc = new WebClient(); 

// .... 

string downloadedFile = wc.DownloadString("http://www.myurl.com/"); 

Cependant, parfois le fichier contient des caractères « intéressants » comme é à é, à ↠et フシギダネ à フシギダãƒ.

Je pense que cela peut avoir quelque chose à voir avec différents types Unicode ou quelque chose, chaque caractère étant changé en 2 nouveaux, peut-être que chaque personnage est divisé en deux mais j'ai très peu de connaissances dans ce domaine. Que pensez-vous est faux?

+1

Le serveur renvoie probablement un mauvais encodage dans l'en-tête 'Content-Type'. – dtb

+4

Vous devriez lire [cet article] (http://www.joelonsoftware.com/articles/Unicode.html) pour avoir une compréhension de base sur Unicode. Cela couvrira toutes les raisons pour lesquelles certains articles apparaissent comme deux, par exemple. Mais surtout, cela vous aidera à comprendre les bases que vous devez savoir sur Unicode. –

+1

Ce très joli HTML UTF-8 vu en ISO-8859-1 ou un autre codage mono-octet. –

Répondre

43

Voici une classe de téléchargement encapsulée qui prend en charge gzip et vérifie l'en-tête de codage et les balises META afin de le décoder correctement. Installez la classe et appelez le GetPage()

public class HttpDownloader 
{ 
    private readonly string _referer; 
    private readonly string _userAgent; 

    public Encoding Encoding { get; set; } 
    public WebHeaderCollection Headers { get; set; } 
    public Uri Url { get; set; } 

    public HttpDownloader(string url, string referer, string userAgent) 
    { 
     Encoding = Encoding.GetEncoding("ISO-8859-1"); 
     Url = new Uri(url); // verify the uri 
     _userAgent = userAgent; 
     _referer = referer; 
    } 

    public string GetPage() 
    { 
     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url); 
     if (!string.IsNullOrEmpty(_referer)) 
      request.Referer = _referer; 
     if (!string.IsNullOrEmpty(_userAgent)) 
      request.UserAgent = _userAgent; 

     request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate"); 

     using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 
     { 
      Headers = response.Headers; 
      Url = response.ResponseUri; 
      return ProcessContent(response); 
     } 

    } 

    private string ProcessContent(HttpWebResponse response) 
    { 
     SetEncodingFromHeader(response); 

     Stream s = response.GetResponseStream(); 
     if (response.ContentEncoding.ToLower().Contains("gzip")) 
      s = new GZipStream(s, CompressionMode.Decompress); 
     else if (response.ContentEncoding.ToLower().Contains("deflate")) 
      s = new DeflateStream(s, CompressionMode.Decompress); 

     MemoryStream memStream = new MemoryStream(); 
     int bytesRead; 
     byte[] buffer = new byte[0x1000]; 
     for (bytesRead = s.Read(buffer, 0, buffer.Length); bytesRead > 0; bytesRead = s.Read(buffer, 0, buffer.Length)) 
     { 
      memStream.Write(buffer, 0, bytesRead); 
     } 
     s.Close(); 
     string html; 
     memStream.Position = 0; 
     using (StreamReader r = new StreamReader(memStream, Encoding)) 
     { 
      html = r.ReadToEnd().Trim(); 
      html = CheckMetaCharSetAndReEncode(memStream, html); 
     }    

     return html; 
    } 

    private void SetEncodingFromHeader(HttpWebResponse response) 
    { 
     string charset = null; 
     if (string.IsNullOrEmpty(response.CharacterSet)) 
     { 
      Match m = Regex.Match(response.ContentType, @";\s*charset\s*=\s*(?<charset>.*)", RegexOptions.IgnoreCase); 
      if (m.Success) 
      { 
       charset = m.Groups["charset"].Value.Trim(new[] { '\'', '"' }); 
      } 
     } 
     else 
     { 
      charset = response.CharacterSet; 
     } 
     if (!string.IsNullOrEmpty(charset)) 
     { 
      try 
      { 
       Encoding = Encoding.GetEncoding(charset); 
      } 
      catch (ArgumentException) 
      { 
      } 
     } 
    } 

    private string CheckMetaCharSetAndReEncode(Stream memStream, string html) 
    { 
     Match m = new Regex(@"<meta\s+.*?charset\s*=\s*""?(?<charset>[A-Za-z0-9_-]+)""?", RegexOptions.Singleline | RegexOptions.IgnoreCase).Match(html);    
     if (m.Success) 
     { 
      string charset = m.Groups["charset"].Value.ToLower() ?? "iso-8859-1"; 
      if ((charset == "unicode") || (charset == "utf-16")) 
      { 
       charset = "utf-8"; 
      } 

      try 
      { 
       Encoding metaEncoding = Encoding.GetEncoding(charset); 
       if (Encoding != metaEncoding) 
       { 
        memStream.Position = 0L; 
        StreamReader recodeReader = new StreamReader(memStream, metaEncoding); 
        html = recodeReader.ReadToEnd().Trim(); 
        recodeReader.Close(); 
       } 
      } 
      catch (ArgumentException) 
      { 
      } 
     } 

     return html; 
    } 
} 
+1

Hey, ça marche. Merci. –

+1

Quelque chose que j'ai écrit l'année dernière pour un projet azur :) Heureux qu'il pourrait être utile pour vous. –

+0

Merci d'avoir partagé ce Mikael. Je l'ai utilisé et j'ai trouvé un problème avec la détection d'encodage. Si les en-têtes contiennent 'charset', il ne devrait pas vérifier la balise meta car les règles de priorité indiquent clairement qu'en cas de conflit, l'en-tête a la plus haute priorité. http://goo.gl/5q0Yg – Diadistis

-4

Essayez cette

string downloadedFile = wc.DownloadString("http://www.myurl.com"); 

i enlever AllWays le dernier "Slash" et cela a fonctionné jusqu'à présent comme un charme. Mais je pourrais être aussi un danger

+5

La barre oblique à la fin de l'URL n'a rien à voir avec l'encodage. –

2

Puisque je ne suis pas autorisé à commenter (réputation insuffisante), je devrais poster une réponse supplémentaire. J'utilise régulièrement la bonne classe de Mikael, mais j'ai rencontré un problème pratique avec la regex qui essaie de trouver la méta-info de charset. Cette

Match m = new Regex(@"<meta\s+.*?charset\s*=\s*(?<charset>[A-Za-z0-9_-]+)", RegexOptions.Singleline | RegexOptions.IgnoreCase).Match(html); 

échoue sur ce

<meta charset="UTF-8"/> 

alors que cette

Match m = new Regex(@"<meta\s+.*?charset\s*=\s*""?(?<charset>[A-Za-z0-9_-]+)""?", RegexOptions.Singleline | RegexOptions.IgnoreCase).Match(html); 

ne fonctionne pas.

Merci, Mikael.

+0

Merci. J'ai changé mon code :) –