Google Feed API から YQL API へ移行したお話

〈追記 2019年1月〉 残念ながら [YQL終了のアナウンス](https://twitter.com/ydn/status/1079785891558653952) がありました。

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">On Jan. 3, 2019, YQL service at <a href="http://query.yahooapis.com">http://query.yahooapis.com</a> will be retired. YQL based services that use <a href="http://query.yahooapis.com">http://query.yahooapis.com</a>, including users of <a href="http://datatables.org">http://datatables.org</a>, will no longer operate. Yahoo Weather API users see the tweet below for info about continuing your service.</p>&mdash; Y! Developer Network (@ydn) <a href="https://twitter.com/ydn/status/1079785891558653952?ref_src=twsrc%5Etfw">December 31, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* * * 

とっても便利だった Google Feed API ですが、復活劇（猶予期間の延長）を経て [2016年12月15日以降は動かなくなるとのアナウンス](https://developers.google.com/feed/) がありました。

外部ブログのフィードを埋め込んでいるサイトをどうしようかと「google feed api alternative」でググると「**Use Yahoo's YQL API**」とのアドバイスをみつけたので、YQL API について調べてみました。

ちなみに、「google feed api 代替」でググると

- 有志が作成されたライブラリを使う
- Googleスプレッドシートを使う
- PHPでがんばる

などの選択肢があがっていました。

## 流れ

1. YQL (Yahoo Query Language) とは
2. YQL API について調べる
3. Google Feed API から YQL API へ
4. 表示の調整とか

## 1. YQL (Yahoo Query Language) とは
[YQL - Yahoo Developer Network](https://developer.yahoo.com/yql/)

![yql_00.png](https://qiita-image-store.s3.amazonaws.com/0/111287/a1c67856-558a-6e47-de4b-e778f859d1be.png)

> YQLとは米Yahoo! Developer Networkが提供している、ありとあらゆるWeb上のリソースにアクセスすることができる開発者向けのサービスです。
> 
> 文法はSQLのようで独特ですが、要約すると
> 
> - XML、HTML、RSS、CSVなどをJSONPに変換
> - WebAPIレスポンスの加工
> - キャッシュ機能つき
> 登録などをしなくても使える(利用制限等についてはYQL公式サイトをご参照ください)
> 
> こういった機能を持つ、汎用的なプロキシサービスです。

簡単に言うと

> 「YQLというものを使うと、XMLをJSONPに変換して扱えるようになる」

ということらしいです。便利っぽい！

参考）[XMLのWebAPIを爆速で使いこなせるフレームワーク - Yahoo! JAPAN Tech Blog](https://techblog.yahoo.co.jp/programming/bakusoku-yql/)

### なぜJSONP？

異なるドメインのデータを引っ張ってくるのに、[XMLHttpRequest](https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest) ではブラウザやサーバのクロスドメイン制約に引っ掛かるため、JSONP を使いましょう、ということらしいです（ざっくり

## 2. YQL API について調べる
YQL、日本ではあまりメジャーではないみたいですね（日本語のドキュメントがないから？
わたしは先人の知恵（参考サイト）に肖りながら進めてゆきます！

### YQL Console を見てみる
まず [YQL Console](https://developer.yahoo.com/yql/console/) を開きます。

![yql_01.png](https://qiita-image-store.s3.amazonaws.com/0/111287/de744573-f5e1-04af-769b-5db51f5e3122.png)

「**show tables**」と表示されているところがYQLのSELECTステートメントを記述する場所です。
今回はブログのRSSフィードのデータが欲しいので、左の **data** から **rss** を選びます。

![yql_02.png](https://qiita-image-store.s3.amazonaws.com/0/111287/c39d496f-7dbf-bece-10b3-a0154c09aea4.png)

ステートメントが自動で置き換わりました。

```sql
select * from rss where url='フィードのURL'
```

が基本形です。
URLをフィードを取得したいものに書き換え、**JSON**形式が選択されていることを確認して「**Test**」ボタンを押します。

selectの後の「*」はワイルドカードです。次のようにデータを指定して取得することもできます。

```sql
select title,link,description from rss where url='フィードのURL'
```

### 取得データを見てみる
「Formatted View」の中身を見ると、どんなデータが取得できるか確認できます。

データは階層（ドットシンタックス）になっています。「Tree View」タブに切り替えると階層がわかりやすいです。

![yql_03.png](https://qiita-image-store.s3.amazonaws.com/0/111287/59477dbb-64ee-e17d-8e10-bd7b0bbb5470.png)

itemの中身がブログの1記事にあたります。

例えば：ブログのタイトルはこんな感じ。

```
query.results.item.title
```

## 3. Google Feed API から YQL API へ
では、なんとなく解ったところでAPIを移行してみます！

```html:表示用HTML
<div id="feed"></div>
```

**Google Feed API**

```html
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load('feeds', '1');

    function initialize() {
        var feed = new google.feeds.Feed('<!-- BEGIN_MODULE Blog_Field -->{blogRssFeedUrl}<!-- END_MODULE Blog_Field -->');
        feed.setNumEntries(1);
        // feed.includeHistoricalEntries();
        feed.load(function(result) {
            if (!result.error) {
                var container = document.getElementById('feed');

                for (var i = 0; i < result.feed.entries.length; i++) {
                    var entry = result.feed.entries[i];

                    var dd = new Date(entry.publishedDate); // now
                    var yearNum = dd.getYear();
                    if (yearNum < 2000) yearNum += 1900;
                    var m = dd.getMonth() + 1;
                    if (m < 10) {
                        m = '0' + m;
                    }
                    var d = dd.getDate();
                    if (d < 10) {
                        d = '0' + d;
                    }
                    var date = yearNum + '.' + m + '.' + d;

                    imgsrc = entry.content.match(/src="(.*?)"/igm);

                    container.innerHTML += '<article class="js-biggerlink">' + '<img ' + imgsrc + ' alt="" class="img-responsive" />' + '<h2><span class="label label-lightgray">Blog</span>' + entry.title + '</h2>' + '<p class="small">' + date + '</p>' + '<p class="feed-content">' + entry.contentSnippet.substring(0, 100) + '...</p>' + '<p><a href="' + entry.link + '" class="btn btn-primary-outline btn-block" target="_blank">View details <i class="fa fa-chevron-right"></i></a></p>' + '</article>';
                }
            }
        });
    }
    google.setOnLoadCallback(initialize);
</script>
```

こんな感じのスクリプトでした。
＊フィードのURLにはCMSの変数が入っています

置き換えてみます。

**YQL API**

```html
<script type="text/javascript">
    $(function(){
        $.getJSON("https://query.yahooapis.com/v1/public/yql?callback=?", {
            q: "select * from rss where url = '<!-- BEGIN_MODULE Blog_Field -->{blogRssFeedUrl}<!-- END_MODULE Blog_Field -->'",
            format: "json"
        }, function(json) {
            var container = document.getElementById('feed');

            for (var i = 0; i < json.query.results.item.length; i++) {
                var entry = json.query.results.item[i];

                var dd = new Date(entry.date); // now
                var yearNum = dd.getYear();
                if (yearNum < 2000) yearNum += 1900;
                var m = dd.getMonth() + 1;
                if (m < 10) {
                    m = '0' + m;
                }
                var d = dd.getDate();
                if (d < 10) {
                    d = '0' + d;
                }
                var date = yearNum + '.' + m + '.' + d;

                imgsrc = entry.encoded.match(/src="(.*?)"/igm);

                container.innerHTML += '<article class="js-biggerlink">' + '<img ' + imgsrc + ' alt="" class="img-responsive" />' + '<h2><span class="label label-lightgray">Blog</span>' + entry.title + '</h2>' + '<p class="small">' + date + '</p>' + '<p class="feed-content">' + entry.description.substring(0, 100) + '...</p>' + '<p><a href="' + entry.link + '" class="btn btn-primary-outline btn-block" target="_blank">View details <i class="fa fa-chevron-right"></i></a></p>' + '</article>';
            }
        });
    });
</script>
```

わたしが取得したいのはライブドアブログだったのですが、デフォルトで5件表示されました。

## 4. 表示の調整とか

今回は **最新の1件だけ** を表示したいので、少し書き換えます。
取得するデータの件数を設定するときはステートメントに記述します。

```sql
select * from rss(オフセット数,アイテム数) where url='フィードのURL'
```

[Remote Limits | SELECT Statement - Yahoo Developer Network](https://developer.yahoo.com/yql/guide/select.html#remote-limits)

例えば、最初の3件を取得したい場合は

```sql
select * from rss(0,3) where url='フィードのURL'
```

もしくは、

```sql
select * from rss(3) where url='フィードのURL'
```

オフセットのデフォルトが0なので、省略することができます。

修正するとこんな感じに。

```html
<script type="text/javascript">
    $(function(){
        $.getJSON("https://query.yahooapis.com/v1/public/yql?callback=?", {
            q: "select * from rss(1) where url = '<!-- BEGIN_MODULE Blog_Field -->{blogRssFeedUrl}<!-- END_MODULE Blog_Field -->'",
            format: "json"
        }, function(json) {
            var container = document.getElementById('feed');

            var entry = json.query.results.item;

            var dd = new Date(entry.date); // now
            var yearNum = dd.getYear();
            if (yearNum < 2000) yearNum += 1900;
            var m = dd.getMonth() + 1;
            if (m < 10) {
                m = '0' + m;
            }
            var d = dd.getDate();
            if (d < 10) {
                d = '0' + d;
            }
            var date = yearNum + '.' + m + '.' + d;

            imgsrc = entry.encoded.match(/src="(.*?)"/igm);

            container.innerHTML += '<article class="js-biggerlink">' + '<img ' + imgsrc + ' alt="" class="img-responsive" />' + '<h2><span class="label label-lightgray">Blog</span>' + entry.title + '</h2>' + '<p class="small">' + date + '</p>' + '<p class="feed-content">' + entry.description + '</p>' + '<p><a href="' + entry.link + '" class="btn btn-primary-outline btn-block" target="_blank">View details <i class="fa fa-chevron-right"></i></a></p>' + '</article>';
        });
    });
</script>
```

無事に1件だけ表示することができました。
これで、2016年12月15日が来ても慌てなくて大丈夫なはず！

## 〈追記〉YQL API の利用制限について
YQLの利用制限についてメモしておきます。

![yql_04.png](https://qiita-image-store.s3.amazonaws.com/0/111287/d2337455-b984-a31b-db4a-82d2cae9d073.png)

[Usage Information and Limits | Overview - Yahoo Developer Network](https://developer.yahoo.com/yql/guide/overview.html#usage-information-and-limits)

APIのコール数が **1時間あたり2,000まで** の場合、APIキーは必要ないようです。Public Endpoint ( /v1/public/* ) が使えます。

それ以上の場合は、APIキーを取得することで **1時間あたり20,000まで** 使えるようになるとのこと（1日あたり100,000まで）。OAuth Endpoint ( /v1/yql/* ) を使います。

【教訓】サイトを常時SSL化した場合、YQL Endpoint が http だとブロックされるので **https** を使う。

## 参考サイト
- [Google Feed API  |  Google Developers](https://developers.google.com/feed/)
- [YQL - Yahoo Developer Network](https://developer.yahoo.com/yql/)
- [javascript - Loading RSS feed with AJAX: alternatives to Google Feed API? - Stack Overflow](http://stackoverflow.com/questions/34049813/loading-rss-feed-with-ajax-alternatives-to-google-feed-api)
- [XMLのWebAPIを爆速で使いこなせるフレームワーク - Yahoo! JAPAN Tech Blog](https://techblog.yahoo.co.jp/programming/bakusoku-yql/)
- [YQL×JSONPでクロスドメイン制約を超え、要素を取得するハナシ | 戯言Vestige++](http://blog.yo41sawada.jp/weblog/tips/yql_cross-domain/)
- [YQLを使用してWordPressのRSSをjsonで取得してjQueryで表示する | webOpixel](http://www.webopixel.net/javascript/150.html)