使ってみよう! Windows Live SDK/API

第32回Live Labs Pivot(2/2)

オリジナルコレクションの作成

今回も前回に続いてLive Labs Pivotです。前回はPivotの使い方とその仕組みについて簡単に紹介しました。今回は、Pivotで閲覧できるコレクションを作成してみましょう。

コレクションは、Collection XML(CXML)とDeep Zoom形式の画像から成っています。どちらもプログラムから生成することが可能ですが、より簡単にコレクションを作成できるようMicrosoft Office Excelのアドインが用意されています。今回はこれを使用したコレクションの作成を紹介します。

Pivot Collection Tool for Excel

コードを書かずにPivotのコレクションを生成できるExcelアドイン、Pivot Collection Tool for Excelは、PivotのWebサイトからダウンロードできます。Excel 2007以降に対応したアドインです。現在、Office 2010のBeta版がダウンロードが可能ですのでOfficeの試用と併せてアドインを使ってみるのもよいかもしれません。

インストールするとExcelのリボンにPivot Collectionsが表示されます。図1はNew Collectionボタンをクリックして新しいコレクションを作成したところです。

図1 コレクションの作成
図1 コレクションの作成

さっそく、コレクションを作成していきましょう。まずNew Collectionボタンをクリックします。クリックにより作成されたシートにコレクションの情報を入力していくことでコレクションを作成できます。

シートの1行がコレクションのひとつのアイテムに対応し、各列がFacet Category、各セルの値がFacetに対応しています。

画像の追加

簡単な例としてWindowsのサンプルピクチャのコレクションを作成します。コレクションに追加する画像は、Image Location列にパスを直接入力するか、Choose Imageボタンから追加またはImport Imagesからまとめて追加します図2⁠。

図2 画像の追加
図2 画像の追加

Image Location列には、httpから始まるURLも入力できます。パスが入力されると自動でPreview列にその画像が表示されます。このPreviewの表示は、Excel操作中に何度も画像の読み込み・表示される場合があるため、その他の項目から入力し最後に画像パスを設定するほうがよいでしょう。また、URLを入力した場合、コレクション生成時に正しく画像をダウンロードできない場合があるようです。画像を一度ダウンロードして、ローカルパスを指定したほうが確実です。

基本情報の入力

コレクションの基本的な情報として、アイテムの名前とリンク先(アイテムから移動できるWebサイト)をName列とHref列に入力します図3⁠。

図3 基本情報の入力
図3 基本情報の入力

名前とリンク先は省略可能です(画像を省略してもコレクションは生成できます⁠⁠。名前とリンク先はFacet Categoryではなく、アイテムの基本的な情報としてCXMLに記述されます。

同様にDescription列にはアイテムの説明を記述します(省略可⁠⁠。この説明もFacet Categoryではなく特別な値として扱われます。入力した内容はインフォパネルの説明欄に表示されることになります図4⁠。

図4 インフォパネル
図4 インフォパネル

参考までにCXMLは次のように記述されています。名前とリンク先はItem要素の属性として、説明はDescription要素として記述されていることがわかります。

<?xml version="1.0" encoding="utf-8"?>
<Collection xmlns:p="http://schemas.microsoft.com/livelabs/pivot/collection/2009" SchemaVersion="1.0" Name="New Collection2" xmlns="http://schemas.microsoft.com/collection/metadata/2009">
  <Items ImgBase="New Collection2_files\evrc05n1.m5u.xml" HrefBase="http://ja.wikipedia.org/wiki/">
    <Item Id="0" Img="#0" Name="菊" Href="%E3%82%AD%E3%82%AF">
      <Description>キク科キク属の植物。</Description>
    </Item>
    ...
  </Items>
</Collection>

コレクションのプレビューと生成

入力が完了したらPivotで閲覧してみましょう。Quick Previewボタンをクリックすると、Deep Zoom形式の画像は生成せずにその他の情報がどのように反映されるかを確認できます図5⁠。

図5 コレクションのプレビュー
図5 コレクションのプレビュー

Publish Collectionボタンをクリックすると、CXMLとDeep Zoom形式の画像ファイル群が生成されます。この結果をWebで公開すればPivotユーザーが閲覧可能です。ぜひ、おもしろいコレクションを公開して、開発元へもフィードバックしていきましょう。

Facet Categoryの追加

以上で一応のコレクションは作成できましたが、Facet Categoryがまったくないコレクションです。アイテムにより多くの情報を付加するにはFacet Categoryを追加する必要があります。Facet CategoryとFacetの追記は、空いている列に情報を追記して行います図6⁠。

図6 Facet Categoryの追加
図6 Facet Categoryの追加

この例では評価というFacet Categoryを追加しました。このようにいくつも列を増やしていくことでFacet Categoryを追加します。Facet CategoryおよびFacetはインフォパネルの表示やフィルターパネルに反映されます図7⁠。

図7 Facet Category・Facetのあるコレクション
図7 Facet Category・Facetのあるコレクション

Facet Categoryに使用できる値の型は、文字列や数値、日付、リンクなどがあります。セルの形式を数値や日付などに設定すると、型の反映が行われます。評価の例では、数値型が反映されフィルターパネルではスライダーによるフィルタリングが可能になっています。ただし、空白セルや西暦1年など突出した値がある場合は文字列型となってしまう場合があります。

ひとつアイテムに対して、同じFacet Categoryに複数の値を持つFacetの設定も可能です。その場合、区切りに「||」を使用して入力するか、同名の列を複数追加します図8⁠。

図8 複数の値の入力
図8 複数の値の入力

複数の値が設定されているアイテムは図9のように表示されます。

図9 インフォパネルでの複数の値の表示
図9 インフォパネルでの複数の値の表示

またFacet Categoryごとに、Facetの値をフィルターパネルに表示するかどうか(フィルタリングに利用するかどうか⁠⁠、インフォパネルに表示するかどうか、キーワードによるフィルタリング時に利用するかどうかを選択が可能です。Facet Categoryに対応する各列にフォーカスがあるときリボンのCategory Propertiesのチェックボックスをチェック・非チェックすることで選択します図10⁠。

図10 Category Properties
図10 Category Properties

コレクション情報の入力

最後にコレクション自体の基本情報の入力についてです。アイテム情報を入力したシートの下段のほうにも注釈があるように、Collection Propertiesシートに切り替えることでコレクションの基本情報を入力できます図11⁠。

図11 コレクションの基本情報の入力
図11 コレクションの基本情報の入力

gihyo.jp 連載記事のコレクション

もう少し大きめのコレクションのサンプルとしてgihyo.jpの連載記事のコレクションを作成してみます図12⁠。Excelを利用することには変わりありませんが、入力するデータはプログラムにより収集します。

図12 gihyo.jp 連載記事コレクション
図12 gihyo.jp 連載記事コレクション

前回、コレクションの種類にはシンプル・リンク・ダイナミックコレクションの3種類があることを紹介しました。Excelのアドインにより作成できるコレクションは、シンプルコレクションです。シンプルコレクションで扱えるアイテム数は3000個程度までです。gihyo.jpの記事数もこの記事の執筆時点では範囲内のようですのでサンプルとしてちょうどよいでしょう。

コレクション作成にあたって

gihyo.jpに連載記事の情報を取得するAPIなどは特にありませんので、HTMLを取得し必要な部分を抽出することになります。簡単なサンプルコードを載せていますが、記事数分だけアクセスすることになります。アクセス過多にならないよう十分注意してください。まったく別の情報源からコレクションを作成してみるのもよいでしょう。

今回のサンプルコードは、Visual Studio 2010のBeta2を利用したコンソールアプリケーションの使用を想定しています。言語もVisual Basicの新しいバージョンの10.0の記述になっています。特に難しいことはしていませんので、古いバージョンでも読み替えは簡単だと思います。

情報の選択

次にコレクションに使用する記事の情報(抽出する情報)を決定します。実際には、この作業の前にコレクションの目的や範囲、想定するユーザーを定義する必要がありますが、省略します。

アイテムの基本情報にあたる名前とリンク先を各記事のWebページのタイトルとURLとします。アイテムの説明は省略することにします。また、アイテムの画像は各連載に対してアイコンが使用されていますので、これを使います。

Deep Zoomの機能を活かすには高解像度な画像がよいですが、今回は文章主体の情報のため難しいですね。Pivot Collection GalleryにあるWikipediaコレクションでは文章も画像化して利用しています。こちらも参考にするとよいかもしれません。

連載記事のURLをみると「http://gihyo.jp/dev/serial/01/wl-sdk/0032」というようにディレクトリが設定されています。このdev部分は連載記事のカテゴリーを表しています。wl-sdk部分は連載ごとの決められている文字列です。便宜上ここでは連載IDと呼ぶことにします。記事のカテゴリーと連載IDのふたつをFacet Categoryにします。さらに記事の掲載された日付と著者を取得し、これらもFacet Categoryとして使います。

情報の抽出

それでは、記事の情報を取得していきます。各記事のURLは、検索エンジン用のXMLファイルからまとめて取得し、カテゴリーと連載IDを抽出します。その後、各記事のURLへアクセスし、Webページのタイトル、日付、著者名を抽出します。

プログラムで使用するクラスを次に示します。連載を表すクラスの中に、その連載の記事が複数ある構成にしています。記事もひとつのクラスになっています。

' 連載クラス
Public Class Serial
    Property Category As String ' カテゴリ
    Property Id As String ' 連載 ID
    Property LogoPath As String ' 画像パス
    Private _Articles As New List(Of Article) ' 連載の各記事
    ReadOnly Property Articles As IList(Of Article)
        Get
            Return _Articles
        End Get
    End Property
End Class

' 記事クラス
Public Class Article
    Property Uri As Uri ' 記事の URL
    Property Title As String ' Web ページのタイトル
    Property Author As String ' 著者
    Property [Date] As DateTime ' 掲載された日付
End Class

次にメイン処理部分を示します。Visual Basicのモジュールに記述します。記事URLを取得後、各ページにアクセスし情報を抽出し、最終的にSerialDictionaryというPrivate変数のコレクションに必要な情報が格納された連載オブジェクトが追加されます。

'Imports 
'Imports System.Net
'Imports System.Text.RegularExpressions

Private SerialDictionary As New Dictionary(Of String, Serial)

Sub Main()
    ' 検索エンジン用の XML 取得
    Dim doc = XDocument.Load("http://image.gihyo.co.jp/sitemap.xml")

    ' 各記事の URL 抽出し、各 URL に対して処理を行う。
    For Each el In doc...
        Dim loc = el..Value
        If Not loc.Contains("/serial/") Then
            ' 連載記事の URL でない場合
            Continue For
        End If

        ' URL を「/」で分割
        Dim values = loc.Substring(16).Split(New Char() {"/"c}, StringSplitOptions.RemoveEmptyEntries)
        If values.Length <= 4 Then
            Continue For
        End If
        Dim category = values(0) ' カテゴリー
        Dim serialId = values(3) ' 連載 ID
        Dim no = values(4) ' 記事番号

        ' 連載オブジェクトの生成 カテゴリー/連載 ID をキーとしたコレクションに追加する
        Dim key = category & "/" & serialId
        If Not SerialDictionary.ContainsKey(key) Then
            SerialDictionary.Add(category & "/" & serialId, New Serial)
            SerialDictionary(key).Category = category
            SerialDictionary(key).Id = serialId
        End If

        ' 記事オブジェクトの生成
        Dim article = New Article With {
            .Uri = New Uri(loc)}
        FillArticleInfo(article) ' URL 以外の記事情報を抽出し、記事オブジェクトに設定する
        SerialDictionary(key).Articles.Add(article) ' 記事コレクションへ追加
    Next
End Sub

上記コードで必要なメソッドの内容を示します。

' 記事オブジェクトに必要な情報を設定
Private Sub FillArticleInfo(ByVal article As Article)
    Dim content = GetContent(article.Uri)
    Dim match As Match

    ' Webページタイトルの抽出
    match = Regex.Match(content, "<title>(?<title>.+)</title>")
    If match.Success Then
        article.Title = match.Groups("title").Value.Trim
    End If

    ' 掲載された日付の抽出
    match = Regex.Match(content, "<p class="" date""="">(?<date>.+)</date></p>")
    If match.Success Then
        article.Date = DateTime.Parse(match.Groups("date").Value)
    End If

    ' 著者名の抽出
    match = Regex.Match(content, "<p class="" author""="">(?<author>.+)</author></p>")
    If match.Success Then
        article.Author = match.Groups("author").Value.Trim
    End If
End Sub

' URL から HTML の内容を返す
Private Function GetContent(ByVal uri As Uri) As String
    Dim content As String = 
    Dim request = DirectCast(WebRequest.Create(uri), HttpWebRequest)
    Using response = request.GetResponse
        Using reader = New System.IO.StreamReader(response.GetResponseStream)
            content = reader.ReadToEnd
        End Using
    End Using
    Return content
End Function

ネットワークにアクセスしていますが、例外処理は入っていませんので、ネットワークの状況やサーバーの状態により例外が発生する場合があります。また、HTMLから情報の抽出には正規表現を用いていますが、今後もこの記述で抽出できるとは限りませんので、参考程度に使用してください。

次に、連載を表す画像を取得するコードです。画像ファイル名は、カテゴリーと連載IDから導き出せませんので、連載を一覧できるページから抽出しています。Mainメソッドの最後に次のメソッドを呼ぶように変更します。

' 画像のダウンロード
Private Sub GetImages()
    Dim folder = System.IO.Path.Combine(My.Application.Info.DirectoryPath, "logos")
    My.Computer.FileSystem.CreateDirectory(folder)

    For i = 0 To 200 Step 40
        Dim matchs = Regex.Matches(GetContent(New Uri("http://gihyo.jp/serial&start=" & i.ToString)),
                                   "<div class="" logo""=""><a href="" http:="" gihyo.jp="" (?=""><category>.*?)/serial/01/(?<s_id>.*?)""><img src="" (?=""><img>.*?)"".*?/></s_id></category></a></div>")
        For Each m As Match In matchs
            Dim category = m.Groups("category").Value
            Dim serialId = m.Groups("s_id").Value
            Dim imgUrl = m.Groups("img").Value
            Dim key = category & "/" & serialId
            If Not SerialDictionary.ContainsKey(key) Then
                Continue For
            End If

            Dim path = System.IO.Path.Combine(folder, serialId & ".png")
            Try
                My.Computer.Network.DownloadFile(imgUrl, path)
            Catch ex As Exception
                ' Ignore
            End Try
            SerialDictionary(key).LogoPath = path
        Next
    Next
End Sub

画像のURLを取得後、ダウンロードもしています。

以上で、必要な情報はすべて抽出・取得できました。これらの情報をクリップボードに書き出し、Excelのシートにペーストできるようにしましょう。次のメソッドをGetImageメソッド呼出し後に、Mainメソッドから呼び出します。

Private Sub SetText()
    Dim sb = New System.Text.StringBuilder
    For Each s In SerialDictionary.Values
        For Each a In s.Articles
            sb.Append(s.LogoPath & vbTab)
            sb.Append(a.Title & vbTab)
            sb.Append(a.Uri.ToString & vbTab)
            sb.Append(s.Category & vbTab)
            sb.Append(s.Id & vbTab)
            sb.Append(a.Author & vbTab)
            sb.Append(a.Date.ToString & vbCrLf)
        Next
    Next
    My.Computer.Clipboard.SetText(sb.ToString)
End Sub

これでタブ区切りのテキストデータができました。このままPivotのコレクションシートにはペーストできませんので、Excelのブックを作成し一時的に別のシートにペーストします。そこから画像パス列部分とそれ以外の部分をわけて、Pivotのコレクションシートにペーストします。すると図13のようになります。

図13 gihyo.jp 連載記事コレクションの作成
図13 gihyo.jp 連載記事コレクションの作成

Facet Categoryの名前も設定しています。また、日付の列はセルの値を日付形式にする必要があります。連載IDの値はキーワードフィルタリング時のみ利用できるようにしました。

さて、Publish Collectionボタンをクリックすればコレクションの完成です。大量の画像を生成する必要があるため、コレクションの生成にはそれなりの時間を要します(筆者の環境では1アイテム0.1秒程度でした⁠⁠。その間はExcelの操作ができなくなりますので注意しましょう。正しく抽出できていないところがあれば、コレクションの作成に失敗やPivotで表示が反映されません。適当な値を入力するかアイテムを削除するかで対応しましょう。

以下にコレクションの表示結果を示します。

図14 結果1
図14 結果1
図15 結果2
図15 結果2

その他のコレクションの作成

今回は、Excelのアドインを使用しましたが、頻繁にコレクションの内容が更新される場合、今回のように人手による作業は非常に手間になります。そのような特性を持つコレクションは、CXMLもDeep Zoom形式の画像もプログラムから生成するのが望ましいでしょう。

プログラムからCXMLの作成は、それほど難しいものではありません。実際に生成されたXMLファイルやPivotのWebサイトを確認しながら作成できると思います。また、Pivot Collection Tools for Pivot by Live LabsというクラスからPivot用のコレクションファイルを生成しようとしているオープンソースプロジェクトも登場しています。こちらも参考になるかもしれません。

Deep Zoom形式の画像生成は、自作するとなると少し手間かもしれませんが情報やツール、ライブラリは豊富です。プログラムとの連携は難しいですが、GUIアプリケーションのDeep Zoom Composerもリリースされています。コマンドラインのツールや.NET Frameworkのライブラリも用意されており、こちらは参考になるのではないかと思います。Live Labs SeadragonのWebサイトからダウンロード可能です。また、このほかにもサードパーティ製のツールが同サイトのCreating Contentで紹介されています。

Deep Zoom形式の画像生成には、時間とCPUパワーが必要です。現在のPivotの仕組みでは動的に画像自体が変化していくコレクションは対象に向いていません。コレクションのひとつ、ダイナミックコレクションも画像はあらかじめ生成しておき、XMLファイルのみ動的に生成することが想定されたものとなっていることに留意しておく必要があるでしょう。

おわりに

Live Labs Pivotについては今回で終了です。いかがでしたでしょうか。このプロジェクトが、この先どうなるかはわかりませんが、おもしろい分野でもあり、おもしろいアプリケーションでもあると思いますので、ぜひ実際にさわって、オリジナルのコレクションを作成してみてください。

おすすめ記事

記事・ニュース一覧