c# - 一个 Http 响应(希伯来文)字符未进行属性编码的特定站点

标签 c# character-encoding http-headers

下面这段话让我觉得很有趣。

首先,几个月来我一直在抓取网站。其中包括 hebrew 站点,并且在从 http 服务器接收 hebrew 字符时没有任何问题。

出于某种原因我很好奇整理了一下,下面这个站点是个异常(exception)。我无法正确编码字符。我尝试通过 Fiddler 模拟我所做的工作请求,但无济于事。我的 c# 请求 header 看起来完全一样,但字符仍然不可读。

我不明白的是为什么我总是能够从其他站点检索到 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 站点(一个有效的站点),响应对象返回 header

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

虽然这个 header 在 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();

最佳答案

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

第一个站点用这个标题响应:

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. 执行网络请求,将响应文档保存在二进制缓冲区中。
  2. 检查内容类型 header (如果存在),如果不存在或未提供字符集,则对编码做一些假设。
  3. 通过解码缓冲区并解析生成的 HTML 来读取响应。
  4. 遇到 <meta http-equiv="Content-Type" /> 时 header ,丢弃所有解码的文本,然后通过将二进制缓冲区解释为以指定编码编码的文本重新开始。

C# HTTP 客户端在第 2 步停止,这是理所当然的。它们是 HTTP 客户端,而不是显示 HTML 的浏览器。他们不关心您的有效负载是 HTML、JSON、XML 或任何其他文本格式。

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

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

您还可以假设您知道某个网站或页面使用的编码,然后强制使用该编码:

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 或站点的编码是完全不安全的。无法保证此假设每次都是正确的。

关于c# - 一个 Http 响应(希伯来文)字符未进行属性编码的特定站点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36327747/

相关文章:

c# - Asp.net MVC路由功能

mysql - 是否尽可能使用 ISO-8859-1 而不是 UTF-8 更好?

javascript - Node.js 缓冲区编码问题

Java - 使用 Socket 通过浏览器下载文件

laravel - Lumen/Laravel 从 Http header 获取参数值

c# - 如何要求枚举作为强类型参数?

c# - MVC 内联代码未在 html 标签中呈现

c# - 统一来自相机的光线转换

mysql - 如何在一个实例中为数据库的多个字符集配置 my.cnf

javascript - Content-Disposition header 允许下载和打开文件