以下一段时间以来一直让我很开心。
首先,我一直在抓网站几个月。其中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();
答案 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有效负载,步骤大致如下:
<meta http-equiv="Content-Type" />
标头时,丢弃所有已解码的文本,然后将二进制缓冲区解释为以指定编码方式编码的文本再次开始。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或站点的编码完全不安全。绝不保证这种假设每次都是正确的。