自分は、2008年の後半から2009年の前半にかけて、Web を作成するのに、PHP を使うのか ASP.NET で C# を使うべきなのか、それとも Java を使うのか迷った時期がありました。ASP.NET MVC がリリースされ、また、CMS も Umbraco が使えるということで最終的には C# を使うということにしました。

気象庁が提供する天気予報、気象警報、地震、津波情報などの気象庁 XML の取得については、PubSubHubbub の受信サーバーを用意する必要があります。自分でも受信サーバーをたてていて、最近C#で天気予報を取得するプログラムを書き始めています。気象庁の資料をみていると PHP のサンプルコードが、平成25年3月に開催された気象庁XML利活用セミナー気象庁XMLを入手しようにあったので、そのサンプルコードを C# を使って書いてみました。

資料のサンプルコードでシンプルな例は以下のとおりです。

<?php 
$method = $_SERVER['REQUEST_METHOD'];

// subscribe (or unsubscribe) a feed from the HUB 
if ($method == 'GET') { 
  $hubmode = $_REQUEST['hub_mode']; 
  $hubchallenge = $_REQUEST['hub_challenge']; 
  if ($hubmode == 'subscribe' || $hubmode == 'unsubscribe')
    // response a challenge code to the HUB
    header('HTTP/1.1 200 "OK"', null, 200);
    header('Content-Type:text/plain');
    echo $hubchallenge;
  }else{
    header('HTTP/1.1 404 "Not Found"', null, 404)
  }
}

// receive a feed from the HUB
if ($method == 'POST') {
  // feed Receive
  $string = file_get_contents("php://input");
  // feed save
  $fp = fopen(dateDateTime.UtcNow.ToString('YmdHis') . "_atom" . ".xml", "w");
  fwrite($fp, $string);
  fclose($fp);
?>

これと同じものを、ASP.NET MVC の C#で書いた例です。

using System;
using System.IO;
using System.Web.Mvc;
using System.Xml;

namespace PuSH.Controllers
{
  public class SubscriberController : Controller
  {
    // subscribe (or unsubscribe) a feed from the HUB
    [HttpGet]
    public ActionResult Index()
    {
      string hubMode = Request.QueryString["hub.mode"];
      string hubchallenge = Request.QueryString["hub.challenge"];
      if (hubMode == "subscribe" || hubMode == "unsubscribe")
        // response a challenge code to the HUB
        return Content(Request.QueryString["hub.challenge"]);
      else
        return NotFound();
    }

    // receive a feed from the HUB
    [HttpPost]
    [ActionName("Index")]
    public ActionResult IndexPost()
    {
      // feed Receive
      var reader = new StreamReader(Request.InputStream);
      string str = reader.ReadToEnd();
      // feed save
      using(var sw = new StreamWriter(DateTime.Now.ToString("yyyyMMddHHmmss) + "_atom.xml"))
      {
        sw.Write(str);
      }
      return Content("");
    }
  }
}

このプログラムだとは、PHP も C#はよく似たものだと思います。C#の方が型の宣言をしないといけないのと、関数名やプロパティ名が長いので、記述量が多くなるように見えますが、型宣言に var が使えてコンパイラーが自動で型を決めてくれるし、関数名等で名前が長いのはVSが補間してくれるので、入力が特に面倒だということはありません。

次に POST処理の部分を、変更したサンプルが紹介されています。コードは以下のとおりです。

// receive a feed from the HUB
if ($method == 'POST') {
  // feed Receive
  $string = file_get_contents("php://input");
  //feed Parse & XML GET
  if(FALSE === ($feed = simplexml_load_string($string)))
  {
    exit("feed Parse ERROR");
  }
  foreach($feed->entry as $entry)
  {
    $url = $entry->link['href'];
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    $fp = fopen(basename($url), "w");
    curl_setopt($ch, CURLOPT_FILE, $fp);
    curl_exec($ch);
    curl_close($ch);
    fclose($fp);
  }
}
?>

これを単純に ASP.NET MVC の C# に変更するだけだったら面白くないので、自分が現在使っているルーチンを紹介したいと思います。

追加と変更部分のみ
using System.ServiceModel.Syndication;
using Nlog;

    private static Logger logger = LogManager.GetCurrentClassLogger();

    [HttpPost]
    [ActionName("Index")]
    public ActionResult IndexPost()
    {
      using (XmlReader xmlReader = new XmlTextReader(Request.InputStream))
      {
        try
        {
          var feed = SyndicationFeed.Load(xmlReader);
          if (feed != null)
          {
            string feedtype = "e"; //定時:regular、随時:extra、地震火山:eqvol、その他:other
            foreach (var link in feed.Links)
            {
              if (link.RelationshipType == "self")
              {
                var uris = link.Uri.Segments;
                feedtype = uris[uris.Length - 1].Split('.')[0]; 
              }
            }
            var xmlWriter = XmlWriter.Create(
              Server.MapPath("~/App_Data/feed/") + feedtype + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + ".xml");
            feed.SaveAsRss20(xmlWriter);
            xmlWriter.Close();
                    
            logger.Info("ID:{0} Update:{1} Type:{2}", feed.Id, feed.LastUpdatedTime, feedtype);
            foreach (var item in feed.Items)
            {
              string title = item.Title.Text;
              string name = item.Authors[0].Name;
              string updatetime = item.LastUpdatedTime.ToString();
              foreach (var link in item.Links)
              {
                logger.Info("{0} {1} Update:{2} uri:{3}", title, name, updatetime, link.Uri);
                switch (title)
                {
                  case "府県週間天気予報":
                    処理
                    break;
                  case "府県天気予報":
                    処理
                    break;
                }
              }
            }
          }
        }
        catch(Exception e1)
        {
          logger.Error("Feed Error:" + e1.Message);
        }
      }
      return Content("");
    }

ログの記録には、NLog を使っています。まだプログラムを始めたばかりなので、ログを取ったり、RSSをファイルに保存したりしていますが、最終的には、ログやファイルへの保存は最低限にしていこうと思っています。ログを取ってみると、気象庁 XML は、1分間に1回処理が動いてフィードが飛んでくることがわかります。ここで最大で約1分間の遅延があること、また、たまにフィードの送信が遅延することがあるので数分間程度のタイムラグがあると思った方がいいということが分かります。

実際にはプログラムができたら、気象庁への登録申請が必要になりますが、登録申請の前にきちんとテストをしておきましょう。Google のハブを使ったテストの方法は、気象庁の電文公開の仕組みのページの情報提供に係る仕様とSubscriberの構築についてというPDFファイルに記載があります。

この例からわかるように Web だけの話だったら、PHP はとてもいい言語だと思います。でも、PHP は Web以外で動かそうと思ったら結構大変です。Web 用のオープンソースのアプリケーションは非常に多いのですが、自分は Web 以外でもプログラムをしたかったので PHP は捨てました。C#は Web の世界でも、PHP に決して劣っているわけではありません。そして、多くのデバイスで動作することが魅力です。Android/iPhone のネイティブアプリが作れる Xamarin に期待しているので、時間ができれば Xamarin も使ってみたいと思います。