目的のwebページが新しく更新されていたらそのwebページを取得するという条件付リクエストをC#でやったのでメモ書きです。

(環境はOS:WindowsXp,開発環境はVisualC#2008EEです)

これができれば、目的のwebページのダウンロードだけでなく、オリジナルの検索エンジンやWebAPIなどにも使えるでしょう。

まず、プログラミング前の予備知識についてです。

HTTPリクエストのリクエストヘッダには「If-Modified-Since」というものがあります。これには日時が指定できるのですが、サーバはこのIf-Modified-Sinceに指定された日時以降にwebページが更新されていたら、通常通りにリクエストを受け付けてステータスコード200OKでwebページをクライアント側に返します。しかし、If-Modified-Sinceに指定された日時以降に更新されていなかったらwebページを返しません(ステータスコードは304[NotModified])。

それでは、C#のプログラミングの説明です。webクライアントはHttpWebRequestクラスを使います。そして最初は「If-Modified-Since」を指定しないでGETリクエストすることにします。

//テストなのでグローバル変数
DateTime DtLastModified;

private void testWebRequest(string sUrl)
{

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sUrl);
req.UserAgent = "C# UserAgent";

HttpWebResponse res = (HttpWebResponse)req.GetResponse();
DtLastModified = res.LastModified; //webページの更新時刻(日本時間)

Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
string html = sr.ReadToEnd();
sr.Close();
st.Close();

textBox1.Text = html;
}

で、いきなりで申し訳ないですが、この部分の説明については「httpプログラミング(ヘッダ情報送信、確認@C#,php) 」の記事を参考にしてください。

ここでサーバから受け取った情報をHttpWebResponseクラスが受けていますが(resオブジェクト)、このHttpWebResponseクラスはLastModifiedというプロパティ(型はDateTime)を持っています。LastModifiedはサーバが返したHTTPレスポンスヘッダのうちの一つでwebページの更新時刻です。ですので、この情報をどこかに保管しておいて、同じwebページの二回目以降のリクエストではこの情報をHTTPリクエストヘッダの「If-Modified-Since」
に指定すればいいわけです。

次に二回目のリクエストのプログラムです。(簡単化のため、見やすくするために上であったwebページを文字列に変換する部分は書いていません。)

private void testWebRequest(string sUrl)
{

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sUrl);
req.UserAgent = "C# UserAgent";
req.IfModifiedSince = DtLastModified; //HTTPリクエストヘッダにIf-Modified-Sinceを指定。

HttpWebResponse res = null;
int iStatus;
try
{
 HttpWebResponse res = (HttpWebResponse)req.GetResponse(); //304の時例外発生
 DtLastModified = res.LastModified; //webページの更新時刻(日本時間)
iStatus = (int)res.StatusCode; //200とか
}
catch (WebException ex)
{
res = (HttpWebResponse)ex.Response;
if (res != null)
{
iStatus = (int)res.StatusCode; //304とか
}
}

}

req.IfModifiedSince = DtLastModified;の部分でHTTPリクエストヘッダにIf-Modified-Sinceを指定しています。
また、今度は例外処理を使っています。これはサーバが304NotModifiedを返す時にWebException例外が発生するからです。(304は最初にも書きましたが、If-Modified-Sinceで指定された時刻以降にwebページに更新がなかった事を表しています)

でも、304の場合、上のサンプルのように例外クラスからHttpWebResponseクラスを取得できます。StatusCodeプロパティでサーバのレスポンスステータスコードを取得できます。これを200とかの番号にするにはintでキャストする必要があります。

おすすめ!ソニー、HPの格安なノートPC(激安中古品や未使用新品・・・)をネットで購入する
格安・激安のHPのPavilionシリーズのノートパソコン
10万円以下の格安・激安Vaioノートパソコン(type X,P,N)
ネット上でのノートパソコン売れ筋ランキング情報

以上で一通りの説明は終了ですが、色々自分でテストをしていて気になった部分について以下に書きます。(もしかしたら間違いがあるかもしれないのであまり信じないように。)

・LastModifiedはHttpWebResponse.LastModifiedプロパティだけでなく、HttpWebResponse.Headersプロパティ内にもLastModifiedがある。
・Headersプロパティ内のLastModified(GMTがついている)はグリニッジ標準時刻。これは日本時刻に対して9時間先行している。実際にサーバーが返してくれる更新時刻はこれだけだと思うけど、日本時刻を簡単に扱えるようにHttpWebResponseクラスがLastModifiedというプロパティを用意してくれるのでしょう。だから私達日本人は普通はHttpWebResponse.LastModifiedプロパティを使えばいいと思います。
・全てのサーバがLastModifiedを返してくれる訳ではない。むしろ、返してくれないサーバのほうが多い気がする。LastModifiedを返してくれないサーバの場合、HttpWebResponse.LastModifiedプロパティには現在時刻(変動している)のDateTimeが入っている。これだとサーバがLastModifiedを返したかとうか区別がつかない。だけど、HttpWebResponse.Headersプロパティ内のLastModifiedはnullになっているのでこちらで区別できる。

また、ここではIf-Modified-SinceにLastModifiedを指定したやり方ですが、Etag,If-None-Matchという物を使っても同じことができます。詳しくは「C#でwebページが更新されてたら取得②EtagとIf-None-Match 」を参考にしてください

・ネットワークの基本HTTPを勉強するためのおすすめ入門書の紹介
今夜わかるHTTP (Network)/上野 宣
¥2,520
Amazon.co.jp


・C#初心者のための入門書の紹介
JIS規格対応 標準C#入門 改訂第2版/矢沢 久雄
¥2,835
Amazon.co.jp



参考サイト
HTTPサーバーからのデータを受け取る(HttpWebResponseクラス)
GMTの有効利用