Http Response(希伯来语)字符的一个特定站点未进行属性编码

时间:2016-03-31 08:15:12

标签: c# character-encoding http-headers

以下一段时间以来一直让我很开心。

首先,我一直在抓网站几个月。其中hebrew个网站也是如此,并且在从hebrew服务器接收http个字符时没有任何问题。

出于某种原因,我非常好奇要理清,以下网站是个例外。我无法正确编码字符。我尝试通过Fiddler模拟我的工作请求,但无济于事。我的c#请求标题看起来完全一样,但仍然无法读取字符。

我不明白为什么我一直能够从其他网站检索hebrew个字符,而从这个特别是我不是。造成这种情况的是什么设置。

请尝试以下示例。

    HttpClient httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0");
    //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html;q=0.9");
    //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.5");
    //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate");

    var getTask = httpClient.GetStringAsync("http://winedepot.co.il/Default.asp?Page=Sale");

    //doing it like this for the sake of the example
    var contents = getTask.Result;

    //add a breakpoint at the following line to check the contents of "contents"
    Console.WriteLine();

如上所述,此类代码适用于我尝试的任何其他以色列网站 - 例如Ynet news site

更新:我在使用Fiddler进行“调试”时发现,ynet网站(有效网站)的响应对象返回标题

Content-Type: text/html; charset=UTF-8

虽然winedepot.co.il的回复中没有此标题

我尝试添加它,但仍然没有区别。

 var getTask = httpClient.GetAsync("http://www.winedepot.co.il");

    var response = getTask.Result;

    var contentObj = response.Content;
    contentObj.Headers.Remove("Content-Type");
    contentObj.Headers.Add("Content-Type", "text/html; charset=UTF-8");

    var readTask = response.Content.ReadAsStringAsync();
    var contents = readTask.Result;
    Console.WriteLine();

1 个答案:

答案 0 :(得分:3)

您遇到的问题是网络服务器对其内容类型撒谎,或者说不够具体。

第一个网站以此标题回复:

Content-Type: text/html; charset=UTF-8

带有此标题的第二个:

Content-Type: text/html

这意味着在第二种情况下,您的客户必须对文本的实际编码进行假设。要了解有关文本编码的更多信息,请阅读The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

.NET的内置HTTP客户端在这方面做得并不好,这是可以理解的,因为它是一个难题。阅读链接文章,了解Web浏览器为了猜测编码而必须经历的麻烦,然后尝试理解为什么您不希望在可编程Web客户端中使用此逻辑。

现在网站为您提供了<meta http-equiv="Content-Type" content="actual encoding here" />标记,这是一个讨厌的解决方法,无需正确配置网络服务器。当浏览器遇到这样的标记时,它将不得不重新开始使用指定的内容类型解析文档,然后希望它是正确的。

假设HTML有效负载,步骤大致如下:

  1. 执行Web请求,将响应文档保存在二进制缓冲区中。
  2. 检查内容类型标头(如果存在),如果它不存在或不提供字符集,请对编码做一些假设。
  3. 通过解码缓冲区并解析生成的HTML来读取响应。
  4. 遇到<meta http-equiv="Content-Type" />标头时,丢弃所有已解码的文本,然后将二进制缓冲区解释为以指定编码方式编码的文本再次开始。
  5. C#HTTP客户端在第2步停止,这是正确的。它们是HTTP客户端,而不是HTML显示浏览器。他们并不关心您的有效负载是HTML,JSON,XML还是任何其他文本格式。

    如果在内容类型响应标头中没有给出字符集,则.NET HTTP客户端默认为ISO-8859-1编码,该编码无法显示页面实际上是字符集Windows-1255 (Hebrew)中的字符编码(或者说,它在相同的代码点有不同的字符)。

    Encoding trouble with HttpWebResponse中提供了尝试从元HTML元素进行编码检测的一些C#实现。我不能保证他们的正确性,所以你必须自己承担风险。我知道当前最高投票的答案实际上在遇到元标记时重新发出请求,这非常愚蠢,因为无法保证第二个响应与第一个响应相同,这只是浪费带宽。

    你也可以做一些假设,你知道某个网站或网页使用的编码,然后强制编码:

    using (Stream resStream = response.GetResponseStream())
    {
        StreamReader reader = new StreamReader(resStream, YourFixedEncoding);
        string content = reader.ReadToEnd();
    }
    

    或者,对于HttpClient:

    using (var client = new HttpClient())
    {
        var response = await client.GetAsync(url);
        var responseStream = await client.ReadAsStreamAsync();
        using (var fixedEncodingReader = new StreamReader(responseStream, Encoding.GetEncoding(1255)))
        {
            string responseString = fixedEncodingReader.ReadToEnd();
        }
    }
    

    但假设特定响应,URL或站点的编码完全不安全。绝不保证这种假设每次都是正确的。