だるろぐ

明日できることは、今日しない。

WebMatrix 3: Windows Store アプリを紹介するヘルパー(未完成)

f:id:daruyanagi:20130616181844p:plain

もしあなたが Windows ストア アプリを作っていて、なおかつ自分のサイトを APS.NET MVC/Web Pages で構築していたならば*1、Web ページに Windows ストア アプリの情報を埋め込みたいと思うかもしれませんね。

公式に oEmbed *2のような仕組みが提供されていればベストなのですが、残念ながらないみたい。仕方ないので、スクレイピングして埋め込み用のコードを取得する HTML ヘルパーを作ってみたいと思います。

f:id:daruyanagi:20130616182655p:plain

#~/AppCode/WindowsStore.cshtml

@using System.Text
@using System.Text.RegularExpressions

@helper GetHtml(string id) {
    const string ENDPOINT = "http://apps.microsoft.com/windows/ja-JP/app/";
    const string PATTERN1 = @"itemprop=""(?<itemprop>[^""]+)"" content=""(?<content>[^""<]+)""";
    const string PATTERN2 = @"(href|src)=""(?<content>[^""<]+)"".+itemprop=""(?<itemprop>[^""]+)""";
    const string PATTERN3 = @"itemprop=""(?<itemprop>[^""]+)""[^"">]*>(?<content>[^<]+)<";
    
    using (var downloader = new WebClient())
    {
        downloader.Encoding = Encoding.UTF8;

        var s = downloader.DownloadString(ENDPOINT + id);

        var result = new Regex(@"background-color:#([0-9a-fA-F]{6});")
            .Matches(s)
            .Cast<Match>()
            .Skip(1).Take(1) // 2番目の background-color を取得
            .Select(_ => new {
                ItemProp = "appColor",
                Content = _.Groups[1].Value,
            })
            .Concat(
                new Regex(PATTERN1).Matches(s).Cast<Match>()
                .Select(_ => new {
                    ItemProp = _.Groups["itemprop"].Value,
                    Content = _.Groups["content"].Value.Trim(),
                })
            )
            .Concat(
                new Regex(PATTERN2).Matches(s).Cast<Match>()
                .Select(_ => new {
                    ItemProp = _.Groups["itemprop"].Value,
                    Content = _.Groups["content"].Value.Trim(),
                })
            )
            .Concat(
                new Regex(PATTERN3).Matches(s).Cast<Match>()
                .Select(_ => new {
                    ItemProp = _.Groups["itemprop"].Value,
                    Content = _.Groups["content"].Value.Trim(),
                })
            )
            // 正規表現が手抜きなのでゴミ取り
            .Where(_ => !string.IsNullOrEmpty(_.Content));

<dl>
    @foreach (var item in result)
    {
        <dt>@item.ItemProp</dt>
        <dd>@item.Content</dd>
    }
</dl>

    }
}

Windows ストアは Microdata*3で構造化されているみたいなので、基本的に itemprop という属性を探して値らしきものを取得するという方法でいいみたい(それでも数パターンの正規表現が必要だけど)。ただ、アプリのブランディングカラーは CSS でべた書きされているので、それは別途スクレイピング(今回は background-color を探して Take と Skip で二番目に現れたモノだけを取得るつという手抜き実装)。

あとはこれを実際に使ってみる。

#~/Default.cshtml

@{
    
}

<!DOCTYPE html>

<html lang="ja">
    <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <meta charset="utf-8" />
        <title>マイ サイトのタイトル</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    </head>
    <body>
        @WindowsStore.GetHtml("86b6ecdc-e810-4aa2-9bdb-bb0da5b34737")
    </body>
</html>

で、結果はこんな感じ(name がダブってたりして、不完全だけど!)。

f:id:daruyanagi:20130616183221p:plain

あとはビューやスタイルをいじっていい感じに仕上げればいい……のだけど、飽きた。

しかも、よく考えたら NancyFx *4で JSON を返す API か何かにすればよかったね。そっちの方が汎用的に使える。