2018/11/23(金)PinterestでPINした画像をGoogleドライブに保存できるようにする

■やりたいこと
PinterestでPINした画像をローカルで閲覧したい。おおむね、お絵描きの参考用途。
・ブラウザ窓だと参考画像として扱いづらい
・貧弱な通信環境でも閲覧できる
・いちいちブラウザ開くのが面倒

やっぱりローカルでアクセスできた方が便利だよね。

■調査と課題
直接Pinterestからデータを引っ張ってくることもできるが、定期実行なり待受の仕組みを立てる必要がある。可用性を高く、一方でできるだけ手間や予算がかからない方法を検討したい。自前サーバがあるのでこれを使ってももちろん良いのだが、可用性であればやはりクラウドに軍配が上がる。(半ば興味本位で)極力クラウドサービスだけで構成することを目指す。

ストレージサービスは一般にローカル同期の仕組みを持っているため、Pinterestからストレージサービスに連携できれば要求が満たせそう。

Pinterestとローカルサービスを繋ぐ方法として、IFTTTなどの連携機能構築サービスが有る。まずはこれを調査した。おおむね、パーソナル用途で機能と使いやすさを両立しているのは2018/10現在下記3つくらい。

・IFTTT(Maker)
・Microsoft Flow
・Integromat

これら全てに、Pinterestインプット・Googleドライブアウトプットアダプタがあった。
なのでこれらを繋げばいいだけに思える。

しかし、いざ使ってみると画像ではなくhtmlファイルが連携される。
どれもPinterestが公式で提供したアダプタらしく、画像そのものを外部サービスに渡さない意図的な作りと思われる。

ちなみに、各サービスの比較としては、
▼機能性
Microsoft Flow > Integromat > IFTTT

▼簡便性
IFTTT > Integromat > Microsoft Flow

高機能な方が一見良さそうだが、この手のクリックアンドポイントロジック構築は高機能にするほど最終的にフローチャート作成サービスになってしまい見通しやメンテナンス性が怪しくなるという特性がある。
複雑なものを作ろうとするほど、どこかの境界でコードを自分で書いたほうが俯瞰性が良くなる。

IFTTTはINPUT(THIS)とOUTPUT(THAT)の接続だけしか選べないが、これくらいがちょうど良いように思う。見通しが悪くならない範囲で足すなら、出力を多数化(3つまでとか)、フィルタを一段、データ加工を簡単なもので1段くらいだろうか(その場合IFTTT以外のサービスないしMakerを選ぶことになる)。

いずれのサービスもコール回数に制限がある。
今回用途では1日10枚以内が目処。
上記に挙げた3つのフリープランでいずれも問題なさそうだった。

将来増えるか減るかなどの見通しは不明。

さて、結局以下のように構成した。

■実装
IFTTT⇒Google Apps Script(GAS)⇒Googleドライブ⇒ローカル(Googleドライブの連携機能)

・IFTTTからpinterestのurlをGASに渡す
・GAS内で、FetchしてPinterestのhtmlを取得
・簡単な正規表現マッチで画像URLおよび属性情報を取得
・画像をFetchしてGoogleドライブへ保存
・ファイル名はID+PINの詳細があればそれをファイル名とする
・第一階層のボード名をフォルダとしてそこに保存する

1) GASで下記スクリプトを作成。作成時に各種認証を要求されるので設定してやる。
function doPost(e) {
  // パラメータの取得
  var jsonString = e.postData.getDataAsString();
  var data = JSON.parse(jsonString);
  var pinUrl = data.pinUrl;
  var boardName = data.boardName;
  
  // フォルダ無ければ作成  
  var rootFolder = DriveApp.getRootFolder();
  var pinterestFolder = null;
  var targetFolder = null;
  var folderIterator = rootFolder.getFoldersByName('pinterest');

  if (folderIterator.hasNext()) {
    pinterestFolder = folderIterator.next();
  } else {
    pinterestFolder = rootDir.createFolder('pinterest');
  }

  targetFolder = pinterestFolder;

  // ボード名が取れていればサブフォルダとしてボードフォルダを作成
  if (boardName) {
    folderIterator = pinterestFolder.getFoldersByName(boardName);
    
    if (folderIterator.hasNext()) {
      targetFolder = folderIterator.next();
    } else {
      targetFolder = pinterestFolder.createFolder(boardName);
    }
  }
  
  // html解析処理
  var pinResponseText = UrlFetchApp.fetch(pinUrl).getContentText();
  var imageUrl = pinResponseText.match(/property="og:image".*content="([^"]*)"/)[1];
  // 128文字まで
  var description = pinResponseText.match(/img alt="([^"]*)"/)[1].substring(0, 128);
  
  var imageFilename = pinUrl.match(/[^\/]*$/)[0];
  
  // 画像取得処理
  var imageResponse = UrlFetchApp.fetch(imageUrl);

  // 拡張子は画像本体のContent-Typeから作成  
  var imageFilenameExt = imageResponse.getHeaders()["Content-Type"].match(/image\/([a-zA-Z]*)/)[1];
  if (imageFilenameExt == 'jpeg') {
    imageFilenameExt = 'jpg';
  }
  
  var imageBlob = imageResponse.getBlob().setName(imageFilename + ' - ' + description + '.' + imageFilenameExt);
  
  targetFolder.createFile(imageBlob);
  
}
IFTTTから呼び出せるようにWebアプリケーションとして公開してやる。


2) IFTTTで、Pinterestのオフィシャルアダプタを使いWebHookでGASに連携する。設定は以下。

▼THIS(INPUT)
Pinterest公式の"New Pin on your board"
Pick a board: "Any Board"

▼THAT(OUTPUT)
IFTTT公式の"Make a web request"
URL: https://script.google.com/macros/s/****/exec
Method: POST
Content Type (optional): application/json
Body (optional):
{"pinUrl":"{{PinURL}}", "boardName": "{{Board}}"}
以上で完成。かなりお手軽。

■まとめと使い勝手について
クラウド、フリープラン、更には認証情報をOAuthの世界で完結させたPinterest画像のローカル保存サービスが構築できた。
目標は達成でき、そこそこ便利に使っている。
ただ、PinterestからIFTTTへの発火に少しラグが有り、Googleドライブへの連携にもラグがあるためあまり即時連携という感じにはならない。

特性を理解して使用しつつ、どうしても即時で必要な場合はChromeの拡張などを利用してダウンロードすることにする。

■IFTTTについて
便利。機能を絞っているところが良い。
もちろん絞ってる分できないことがでてくる。
直感的に苦手なことはわかるので、あとは自分が使う上でどういったところでIFTTTで完結できないのか何を組み合わせればよいのかを今後確認していく。

■Google App Script(GAS)について
ロジックを作る必要があり、アウトプットがGoogle関係であればこれ一択。
Javascriptで大体何でもできるし、ドキュメントの必要十分さ、特に開発環境の素晴らしさはワンダフル。

現状無料で使えてるが、将来的に有料化or閉鎖しないかはちょっと心配。あとは制限。
作るときはフェッチの制限を忘れていたがやっぱりあるよね。
Google_Apps_Scriptの無料制限メモ
今回の内容だとURL Fetch 100MBが制限になりそう。
この仕組に限定すれば数百枚くらいは耐えるだろう。

AWSで相当するのは、Amazon API Gateway + Lambdaだろうか。
こいつらは本番運用には良いが、作って動かすまでのハードルが低くない。
こういったミニサービスや、使い捨てのテストAPIなどを作りたい場合GASのほうが向くように思う。


■今後の課題
サービスの選択・組み合わせを継続的に知識アプデするのと、GASがかなりawesomeだったのでできることを覚えたい。


■結論
最近のグルーサービスすげえね
OK キャンセル 確認 その他