私が作成しているスクリーンスクレイピングツールのためにWebからダウンロードしているコンテンツに問題があります。
以下のコードでは、Webクライアントのダウンロード文字列メソッドから返された文字列は、いくつか(すべてではない)のWebサイトのソースダウンロードに対して奇妙な文字を返します。
最近、以下のようにhttpヘッダーを追加しました。以前は、ヘッダーなしで同じコードが同じ効果のために呼び出されていました。 「Accept-Charset」ヘッダーのバリエーションを試したことはありません。基本以外のテキストエンコーディングについてはあまり知りません。
私が参照する文字または文字シーケンスは次のとおりです。
"ï"¿"
そして
"Â"
これらの文字は、Webブラウザーで「ソースの表示」を使用する場合は表示されません。これの原因は何ですか?また、どのように問題を修正できますか?
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);

は、オクテットEF BB BF
のwindows-1252表現です。これは TF-8バイトオーダーマーカー です。これは、リモートWebページがUTF-8でエンコードされているが、windows-1252のように読んでいることを意味します。 ドキュメントによれば 、WebClient.DownloadString
は、リモートリソースを文字列に変換するときに、エンコードとして Webclient.Encoding
を使用します。 System.Text.Encoding.UTF8
に設定すると、理論的には動作するはずです。
WebClient.DownloadString
の実装方法は非常に馬鹿です。応答のContent-Type
ヘッダーから文字エンコーディングを取得する必要がありますが、代わりに、開発者が予想されるエンコーディングを事前に通知することを期待しています。このクラスの開発者が何を考えていたのかわかりません。
応答のContent-Type
ヘッダーからエンコード名を取得する補助クラスを作成しました。
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
はカスタム例外クラスです。InvalidOperationException
または必要に応じて自由に置き換えてください)
次に、WebClient
クラスの次の拡張メソッドがトリックを行います。
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);
}
}
したがって、あなたの例では:
urlData = wc.DownloadStringAwareOfEncoding(uri);
...以上です。
var client = new WebClient { Encoding = System.Text.Encoding.UTF8 };
var json = client.DownloadString(url);
私の場合、返されたデータはgzipで圧縮されており、最初に解凍する必要があったため、この回答は役に立ちました。
私の場合、ユーザーエージェントとCookieを除き、言語、文字セットなどに関連するヘッダーを削除しました。出来た..
// 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");
「www.yahoo.com」などの特別なWebサイトでは、それらのどれも機能しませんでした。問題を解決する唯一の方法は、DownloadString
をOpenRead
に変更し、サンプルコードのようなUserAgent
ヘッダーを使用することでした。ただし、「www.varzesh3.com」のようないくつかのサイトは、どの方法でも機能しませんでした!
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();