YQL Open Data Table Reference Sample メモ その2

YouTube Video Search。これが分かれば基本的な物は作れるはず。

<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
  <meta>
    <author>Guilherme Chapiewski <guilherme.chapiewski@gmail.com></author>
    <documentationURL>http://code.google.com/apis/youtube/2.0/developers_guide_protocol_api_query_parameters.html</documentationURL>
    <sampleQuery>select * from {table} where query="louis ck"</sampleQuery>
    <sampleQuery>select * from {table} where query="basketball" and start_index=11 and  max_results=10</sampleQuery>
    <sampleQuery>select * from {table} where query="apple" and order_by='relevance'</sampleQuery>
  </meta>
  <bindings>
    <select itemPath="results.video" produces="XML">
      <urls>
        <url>http://gdata.youtube.com/feeds/api/videos</url>
      </urls>
      <inputs>
        <key id="q" as="query" type="xs:string" paramType="query" required="true"/>
        <key id="start-index" as="start_index" type="xs:integer" paramType="query" required="false"/>
        <key id="max-results" as="max_results" type="xs:integer" paramType="query" required="false"/>
        <key id="orderby" as="order_by" type="xs:string" paramType="query" required="false"/>
        <key id="key" as="key" type="xs:string" paramType="query" required="false"/>
        <key private="true" id="v" type="xs:integer" default="2" paramType="query"/>
      </inputs>
      <execute>
      (詳細はexecuteの項目で)
      </execute>
    </select>
  </bindings>
</table>

url

よく見るとqueryの指定がない。Sampleが間違ってるんだろうかと思い色々やってみた結果、別に間違ってないらしい。つまり、keyのparamTypeにqueryが指定されている場合urlに自動で補完されるようだ。じゃあpathはどうなのかというと自動補完なんかしてくれない。何処に補完すれば良いのか分からないので当然と言えば当然。

key

key要素は前回説明したけど、今回は前回にはなく何に使うのか一目で分からない属性がついてる。

as


という定義の仕方をすると当然where句においてもwhere q="hoge"という指定の仕方となる。これでも別に構わないと言えばその通りだがいまいち分かりにくい。出来ればwhere query="hoge"としたい。しかしidはURLのquery文字列となるので変更出来ない。そこでasでkeyの別名を指定する。as="query"とした場合、where query="hoge"と出来る。

url要素内での使い方
key id="q" as="query" paramType="query"とした場合
http://hoge.com?{q}
http://hoge.com?{query}
key id="q" as="query" paramType="path"とした場合
http://hoge.com/{q}
× http://hoge.com/{query}
pathとasの組み合わせにおいてのみNGというよく分からない結果となる。

execute要素内での使い方
paramTypeに関係なくasで指定した文字列を使う。asが指定されてる状態でidの方を使うと、そんなものは定義されてないと怒られる。

上記のよく分からない結果はおそらくバグなんだろう。まあpathなんか使う事はあまりないが。

execute

JavaScriptっぽい言語を使ってdataを加工する。JavaScriptっぽい何かであってJavaScriptではない。事実色々存在しないメソッドがあるし。その辺はやりながら覚えるしかない。

if (order_by) {
  if (order_by.length<=0) {
    order_by="relevance";
  }
} else {
  order_by="relevance";
}
var atom = Namespace("http://www.w3.org/2005/Atom");
var media = Namespace("http://search.yahoo.com/mrss/");
var gd = Namespace("http://schemas.google.com/g/2005");
var yt = Namespace("http://gdata.youtube.com/schemas/2007");
var xml = request.get().response,
results = <results></results>;
if (xml) {
  for each (video_entry in xml.atom::entry) {
    var video = <video></video>,
    categories = <categories></categories>,
    tags = <tags></tags>,
    thumbnails = <thumbnails></thumbnails>,
    files = <files></files>;
    video.appendChild(<id>{video_entry.media::group.yt::videoid.toString()}</id>);
    video.appendChild(<url>{video_entry.media::group.media::player.@url}</url>);
    video.appendChild(<title>{video_entry.atom::title.toString()}</title>);
    video.appendChild(<content>{video_entry.media::group.media::description.toString()}</content>);
    video.appendChild(<author>{video_entry.atom::author.atom::name.toString()}</author>);
    video.appendChild(<duration>{video_entry.media::group.yt::duration.@seconds}</duration>);
    video.appendChild(<comment_count>{video_entry.gd::comments.gd::feedLink.@countHint}</comment_count>);
    for each (category in video_entry.media::group.media::category) {
      categories.appendChild(<category>{category.@label}</category>);
    }
    video.appendChild(categories);
    for each(tag in video_entry.media::group.media::keywords.split(',')) {
      tags.appendChild(<tag>{tag}</tag>);
    }
    video.appendChild(tags);
    for each (thumbnail in video_entry.media::group.media::thumbnail) {
      var t = <thumbnail>{thumbnail.@url}</thumbnail>;
      t.@height = thumbnail.@height;
      t.@width = thumbnail.@width;
      t.@time = thumbnail.@time;
      thumbnails.appendChild(t);
    }
    video.appendChild(thumbnails);
    for each (file in video_entry.media::group.media::content) {
      var f = <file>{file.@url}</file>;
      f.@type = file.@type;
      files.appendChild(f);
    }
    video.appendChild(files);
    results.appendChild(video);
  }
}
response.object = results;

そして最初のSampleQueryを実行するとこんなXMLが返ってくる。

<?xml version='1.0' encoding='UTF-8'?>
  <feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:gml='http://www.opengis.net/gml' xmlns:yt='http://gdata.youtube.com/schemas/2007' xmlns:georss='http://www.georss.org/georss'>
  <id>http://gdata.youtube.com/feeds/api/videos</id>
  <updated>2013-04-08T02:48:04.454Z</updated>
  <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
  <title type='text'>Videos matching: louis ck</title>
  <logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
  <link rel='alternate' type='text/html' href='http://www.youtube.com'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos'/>
  <link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/batch'/>
  <link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos?q=louis+ck&amp;start-index=1&amp;max-results=25'/>
  <link rel='next' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos?q=louis+ck&amp;start-index=26&amp;max-results=25'/>
  <author>
    <name>YouTube</name>
    <uri>http://www.youtube.com/</uri>
  </author>
  <generator version='2.1' uri='http://gdata.youtube.com'>YouTube data API</generator>
  <openSearch:totalResults>1000000</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <entry>
    <id>http://gdata.youtube.com/feeds/api/videos/1kVqL6X5jXk</id>
    <published>2013-03-12T18:57:48.000Z</published>
    <updated>2013-04-08T01:54:28.000Z</updated>
    <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/categories.cat' term='People' label='ブログ'/>
    <title type='text'>Louis C.K. | Live at the Beacon Theater (2011)</title>
    <content type='text'>If you really like his shows head on over to https://buy.louisck.net, to support his future shows. Louis C.K. | Live at the Beacon Theater (2011) Copyright O...</content>
    <link rel='alternate' type='text/html' href='http://www.youtube.com/watch?v=1kVqL6X5jXk&amp;feature=youtube_gdata'/>
    <link rel='http://gdata.youtube.com/schemas/2007#video.responses' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/1kVqL6X5jXk/responses'/>
    <link rel='http://gdata.youtube.com/schemas/2007#video.related' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/1kVqL6X5jXk/related'/>
    <link rel='http://gdata.youtube.com/schemas/2007#mobile' type='text/html' href='http://m.youtube.com/details?v=1kVqL6X5jXk'/>
    <link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/1kVqL6X5jXk'/>
    <author>
      <name>Sarviin Ageelen</name>
      <uri>http://gdata.youtube.com/feeds/api/users/Xcess96</uri>
    </author>
    <gd:comments>
      <gd:feedLink rel='http://gdata.youtube.com/schemas/2007#comments' href='http://gdata.youtube.com/feeds/api/videos/1kVqL6X5jXk/comments' countHint='775'/>
    </gd:comments>
    <georss:where>
      <gml:Point>
        <gml:pos>5.391829967498779 100.38143157958984</gml:pos>
      </gml:Point>
    </georss:where>
    <yt:hd/>
    <media:group>
      <media:category label='ブログ' scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>People</media:category>
      <media:content url='http://www.youtube.com/v/1kVqL6X5jXk?version=3&amp;f=videos&amp;app=youtube_gdata' type='application/x-shockwave-flash' medium='video' isDefault='true' expression='full' duration='3764' yt:format='5'/>
      <media:content url='rtsp://v6.cache1.c.youtube.com/CiILENy73wIaGQl5jfmlL2pF1hMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='3764' yt:format='1'/>
      <media:content url='rtsp://v6.cache1.c.youtube.com/CiILENy73wIaGQl5jfmlL2pF1hMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='3764' yt:format='6'/>
      <media:description type='plain'>If you really like his shows head on over to https://buy.louisck.net, to support his future shows. Louis C.K. | Live at the Beacon Theater (2011) Copyright O...</media:description>
      <media:keywords/>
      <media:player url='http://www.youtube.com/watch?v=1kVqL6X5jXk&amp;feature=youtube_gdata_player'/>
      <media:thumbnail url='http://i.ytimg.com/vi/1kVqL6X5jXk/0.jpg' height='360' width='480' time='00:31:22'/>
      <media:thumbnail url='http://i.ytimg.com/vi/1kVqL6X5jXk/1.jpg' height='90' width='120' time='00:15:41'/>
      <media:thumbnail url='http://i.ytimg.com/vi/1kVqL6X5jXk/2.jpg' height='90' width='120' time='00:31:22'/>
      <media:thumbnail url='http://i.ytimg.com/vi/1kVqL6X5jXk/3.jpg' height='90' width='120' time='00:47:03'/>
      <media:title type='plain'>Louis C.K. | Live at the Beacon Theater (2011)</media:title>
      <yt:duration seconds='3764'/>
    </media:group>
    <gd:rating average='4.918944' max='5' min='1' numRaters='3257' rel='http://schemas.google.com/g/2005#overall'/>
    <yt:statistics favoriteCount='0' viewCount='269442'/>
  </entry>
  ・・・・
</feed>

script部分を上から見ていく。

var atom = Namespace("http://www.w3.org/2005/Atom");
var media = Namespace("http://search.yahoo.com/mrss/");
var gd = Namespace("http://schemas.google.com/g/2005");
var yt = Namespace("http://gdata.youtube.com/schemas/2007");

Namespaceを複数定義している。レスポンスのXMLを見ると確かに名前空間が複数ある。流石にXMLを扱うだけあって独自実装で名前空間を扱えるようにしてるようだ。

var xml = request.get().response

今はとりあえずこれでurl要素で定義したurlのレスポンスが返ってくると覚えとけば良い。headerメソッドやpostメソッドもある。

for each (video_entry in xml.atom::entry)

JavaScriptでforeachか。まあ要はfor in loopだろうと思ったら挙動がちょっと違う。for in loopの場合指定されたオブジェクトのプロパティを列挙するが、このforeachは指定されたオブジェクト自身を列挙する。この場合はentryの子要素ではなくentry自身を順番に返す。foreachってそんな挙動だったっけ。
あと「xml.atom::entry」という指定の方法。暫く眺めてれば分かると思うけど、名前空間の指定はこうやるらしい。このSampleの様に名前空間が複数でなく1つであろうとも必ず名前空間の指定は必要。1つしかないしわかり切ってるので省略可なんて事にはならない。

video.appendChild(<id>{video_entry.media::group.yt::videoid.toString()}</id>);
video.appendChild(<url>{video_entry.media::group.media::player.@url}</url>);
video.appendChild(<title>{video_entry.atom::title.toString()}</title>);
video.appendChild(<content>{video_entry.media::group.media::description.toString()}</content>);
video.appendChild(<author>{video_entry.atom::author.atom::name.toString()}</author>);
video.appendChild(<duration>{video_entry.media::group.yt::duration.@seconds}</duration>);
video.appendChild(<comment_count>{video_entry.gd::comments.gd::feedLink.@countHint}</comment_count>);

さて、この余り見覚えのない記法はECMAScript for XML(E4X)という奴らしい。こんな物始めて知った。全てをDOMオブジェクトで扱うという世の中の流れの中で違和感がある。具体的な実装の説明はここら辺
{}は変数の代わりに使うって書いてあるが、要するに文字列や数値のベタ打ち以外は全部{}で括る。@urlはXPATHで出てくるのと一緒でattributeを表す。この{}はE4Xという記法の中で使うのであっていつもいつも{}で括るわけではない。例えば

video.appendChild(video_entry.atom::title.toString());

とする場合はXMLタグで囲まれてないので{}も必要ない。
ちなみに、「video_entry.atom::title」は「<a class="keyword" href="http://d.hatena.ne.jp/keyword/value">value</a>」というE4X文字列を返すが、「video_entry.atom::title.toString()」とすると「value」のみを返す。