togetterで1ヶ月以上前のツイートをまとめる、大量の短縮URLを展開する技

以前に書いた記事でTwitterハッシュタグをまとめようと思いtogetterでやろうとしたら、2週間前までしか表示されず、詰んでました。
matatsuna.hatenablog.com

まず、検索したら以下のサイトを使って過去のツイートをさかのぼる的な感じの記事がたくさん見つかったので
http://topsy.com/
アクセスしてみると
あ、あれ?閉鎖されてる?
www.itmedia.co.jp
マジか...

リンクを取得すればtogetterでインポートができる。
さて、どうやってリンクを取得しようか?
リンクってhtmlの中にあるんだよね?
じゃあ、htmlを解析したらリンクわかるんじゃね?
ってことでまずhtmlを取得します。
twitter.com
ここを使って目的のツイートをすべて読み込みます。(下にスクロースし続けます)
3000件読み込むのに10分弱かかりました。
読み込んだら右クリックでhtmlを保存します。
この保存にもかなり時間がかかりました。
読み込んでhtmlの中身を見てみると以下のような部分が繰り返しあるのとaタグのhrefのなかに短縮URLの形でそのツイート自体のリンクが入ってることがわかります。

<div class="js-tweet-text-container">
<p class="TweetTextSize  js-tweet-text tweet-text" lang="ja" data-aria-label-part="0">学校では、一太郎スマイルよく使ってたな~
<a href="https://t.co/MKDWhhtoos" rel="nofollow" dir="ltr" data-expanded-url="http://pictureadd.azurewebsites.net" class="twitter-timeline-link" target="_blank" title="http://pictureadd.azurewebsites.net" ><span class="tco-ellipsis"></span><span class="invisible">http://</span><span class="js-display-url">pictureadd.azurewebsites.net</span><span class="invisible"></span><span class="tco-ellipsis"><span class="invisible">&nbsp;</span></span></a> <a href="/hashtag/pictureadd?src=hash" data-query-source="hashtag_click" class="twitter-hashtag pretty-link js-nav" dir="ltr" ><s>#</s><b><strong>pictureadd</strong></b></a> <a href="https://t.co/g3xF1VDYXK" class="twitter-timeline-link u-hidden" data-pre-embedded="true" dir="ltr" >pic.twitter.com/g3xF1VDYXK</a></p>
</div>

このリンクだけを正規表現で取り出せばいいのでは?とも思ったのですが、あまり慣れてないので、processingで文字列検索をして取り出すことにしました。

String [] oritxt;
String hoge="";
void setup() {
oritxt=loadStrings("index.txt");
for (int i=0; i<oritxt.length; i++) {
 hoge=oritxt[i];
  if (hoge.length()>200) {//200文字以上でtrue
  int target= hoge.lastIndexOf("https://t.co/");
   if (target>1) {//https//t.coが含まれてたらtrue
    println(hoge.substring(target, target+23));
   }
  }
 }
println("END");
}

一回コードを書くだけなのできれいなコードとかは全く考えてないです。
これでリンクの取得ができました。
次はリンクの展開です。
複数リンクを展開するツールってのがなかなか見つからなかったのですが以下の記事を参考にすることにしました。
tech.cookbiz.co.jp
この中にあるスクリプトを張り付けて実行したところ動かない!
(もしかしたら動いてるかも?)
個人的に以下のように改良しました。(展開に使うサイトを変えました)

function tenkai(originalUrl) {
  var apiurl = "http://api.hitonobetsu.com/surl/open?url=" + encodeURIComponent(originalUrl); // (1)使うAPIを指定
  var response = UrlFetchApp.fetch(apiurl);
  var content = Utilities.jsonParse(response.getContentText("UTF-8"));
  var urls = content.original; // (2)JSONの戻り値からurlsだけを取得
  //urls.shift();  // APIの一番目は元のURLだったので要らない。先頭要素を削除
  return urls; // (3)スプレッドシートに出力(縦バージョン)
}

これで大量の短縮URLも少しづつ展開されて表示されます。
無事に元のアドレスがわかったのであとはtogetterのツイート読み込みのところに
f:id:matatsuna:20160712090027p:plain
リンクをすべて投下してやるだけでできます。
この方法で作成したまとめはこれです。
togetter.com
時間があったら大量のリンクを展開するwebサービスを作ってもいいかなと思ったので、気が向いたら実装します。

processingを使ってweb上で画像を加工してTwitterで画像とソースコードを共有できる #pictureAlive を作った

前回までphpをメインに使って開発してましたが、今回はJavaScriptです。

表題にあるようにweb上で簡単に画像編集できるツールをおねき (@revC_Rain) | Twitterと一緒に作りました。

http://wada.nkmr.io/pictureAlive/

https://nkmr.io/picturealive/
なお、一部機能は使えなくなっています。追記ここまで(2018/05/20)

自分が担当した主な部分を紹介します。

概要

  • 画像の編集をprocessingのプログラムを使ってweb上で行う
  • 作った画像をツイートできる
  • 書いたソースコードを共有できる

画像の編集をprocessingのプログラムを使ってweb上で行う

今回一番苦労したのがこの部分です。
processingをブラウザで動かすライブラリは大きく以下の二つがあると思います。
Processing.js
p5.js | home
今回はprocessing初心者にも使ってもらえるように書き方がprocessingと同じprocessingjsを使用することにしました。
以下のサイトを参考にしました。
Processing.js の練習場
ところがこれ、エディター部分にjsのコードを書くとそのまま動いてしまうというセキュリティー的にあまりよくないので対策したいと思い、先輩に聞いたところ
「iframe使ってsandbox入れたらいいんじゃない?」
というお話をいただいて、サンプルをいただきました。
iframeとsandboxについて
HTML5/埋め込み/iframe要素 インラインフレーム内のコンテンツに制限をかける - TAG index
ざっくり説明するとページの中にセキュリティー的な制限をかけたページを表示するってことです。(たぶん)
さて、どうやってテキストエリアに書かれたプログラムコードをiframeに送るかというと

var iframe       = document.getElementById('iframe');//iframeを取得
var head         = document.createElement('head');
var body         = document.createElement('body');
var processingjs = document.createElement('script'); // processing.jsの読み込み
var canvas       = document.createElement('canvas'); // canvasタグ
var usercode     = document.createElement('script'); // ユーザーのソースコードが入るタグ

processingjs.setAttribute("src", "http://wada.nkmr.io/pictureAlive/lib/processing2.js");//src属性に代入
usercode.setAttribute("type", "application/processing");//type属性にprocessingであることを代入

usercode.innerHTML = editor.getValue();//テキストエリアに書かれたコードを入れる
head.innerHTML = processingjs.outerHTML;//headに入れるスクリプトタグなどを追加
body.innerHTML = usercode.outerHTML+canvas.outerHTML;//描画するためのコードとcanvasを追加

iframe.setAttribute('src', 'data:text/html;charset=utf-8;base64,' + Base64.encode(head.outerHTML+body.outerHTML));//iframeに作ったコードを追加

このような感じでテキストエリアに書かれたコードをiframeの中に追加して実行させます。
(実際に上記のコードを実行させるにはprocessingjsの初期化の処理などのコードが必要です。)
タグ関係の命令はググってもらえれば分かると思いますが、

iframe.setAttribute('src', 'data:text/html;charset=utf-8;base64,' + Base64.encode(head.outerHTML+body.outerHTML));//iframeに作ったコードを追加

ここの部分のdata:txt/html~~~
の部分について説明したいと思います。
まず、この書き方自体はData URI スキームと呼ばれてます。
http://engineer.blue-corporation.jp/dev/data-uri-scheme/
この記事を読んでもらえれば理解できると思います。

data:text/html;charset=utf-8;base64,

この部分はData URIスキームのhtmlのテキストで文字コードUTF-8base64で変換
という意味です。
どうしてわざわざutf-8base64変換したかというと日本語で書かれたprocessingのtext命令を反映したかったからです。
そうしないと文字化けしてきれいに表示されないです。
標準のbtoa()はアスキー文字(英数字)しか対応してないためライブラリを追加しました。
github.com
(urlエンコードかけてからbtoaしてもだめっだった)
これでリアルタイムにprocessing.jsを実行させることができます。

作った画像をツイートできる

サイトにアクセスしたらまず、タイムスタンプから生成した識別番号を付与します。
iframeにsandboxつけた状態でcanvasの中身を取得することはできないのでsetIntervalを使って数秒ごとに一度スクリーンショットを撮影します。
データベースに識別番号とスクリーンショットを一緒に追加します。
iframeの外にあるツイートボタンが押されたら、識別番号を使ってデータベースに最新の画像の問い合わせを行います。
その画像を以下のアカウントでつぶやきます。
pictureAlive (@picture_Alive) | Twitter
つぶやいた画像のURLを取得して、個人のアカウントをintentという機能を使ってつぶやきます。
cartman0.hatenablog.com
本文(text)に画像のURLを入れて、URLにサイトとのURLを入れればOKです。
そうすると本人がつぶやいたように見えます。
画像だけつぶやいた例


個人のアカウントから見た例
実はこの機能、「ついすた」を参考に実装しました。
twitterスタンプの #ついすた

書いたソースコードを共有できる

ツイートされたリンクをクリックするとその写真が作られた状態でのソースコードごと共有されてます。
リンクにソースコードをgetで渡して上げることでソースコードの共有を可能にしてます。
ところが、URLが長過ぎるとapache側に怒られてアクセスできなくなります。
d.hatena.ne.jp
そこで2000字を超えた場合はデータベースでソースコードを保存することにしています。
最初からそうしたら良かったと今になって後悔してます....

その他

  • アップロードした画像もTwitterでつぶやく
  • 外部urlの画像をprocessing.jsで読み込む
  • urlエンコードの違い
  • phpのヘッダーにも追加
  • Ajaxのためだけに使用したライブラリー

アップロードした画像もTwitterでつぶやく

つぶやいてURLを発行することでファイルサーバーの代わりをさせてます。

外部urlの画像をprocessing.jsで読み込む

iframeの中のcanvasに外部のURLの画像を持ってくることはクロスオリジン制約に引っかかってしまって普通は読み込むことは禁止されてます。
d.hatena.ne.jp
でも今回、読み込まないといけないのでprocessing.jsの中身をすこし書き換えました。

    p.loadImage = function(file, type, callback) {
      // if type is specified, we just ignore it

      var pimg;
      // if image is in the preloader cache return a new PImage
      if (curSketch.imageCache.images[file]) {
        pimg = new PImage(curSketch.imageCache.images[file]);
        pimg.loaded = true;
        return pimg;
      }
      // else async load it
      pimg = new PImage();
      var img = document.createElement('img');

      pimg.sourceImg = img;

      img.onload = (function(aImage, aPImage, aCallback) {
        var image = aImage;
        var pimg = aPImage;
        var callback = aCallback;
        return function() {
          // change the <img> object into a PImage now that its loaded
          pimg.fromHTMLImageData(image);
          pimg.loaded = true;
          if (callback) {
            callback();
          }
        };
      }(img, pimg, callback));

      img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
      return pimg;
    };

https://github.com/processing-js/processing-js/blob/master/processing.js#L18917
これの最後から4行目に少しコードを追加します。

    p.loadImage = function(file, type, callback) {
      // if type is specified add it with a . to file to make the filename
      if (type) {
        file = file + "." + type;
      }
      var pimg;
      // if image is in the preloader cache return a new PImage
      if (curSketch.imageCache.images[file]) {
        pimg = new PImage(curSketch.imageCache.images[file]);
        pimg.loaded = true;
        return pimg;
      }
      // else async load it
      pimg = new PImage();
      var img = document.createElement('img');
      

      pimg.sourceImg = img;

      img.onload = (function(aImage, aPImage, aCallback) {
        var image = aImage;
        var pimg = aPImage;
        var callback = aCallback;
        return function() {
          // change the <img> object into a PImage now that its loaded
          pimg.fromHTMLImageData(image);
          pimg.loaded = true;
          if (callback) {
            callback();
          }
        };
      }(img, pimg, callback));
      img.crossOrigin = "Anonymous";
      img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
      return pimg;
    };

これで大丈夫になるはずです。

urlエンコードの違い

今回ソースコードをurlに埋め込むということで、エンコードをjs側でデコードをphp側で処理してたらある問題が起こりました。


はじめ、訳がわかりませんでした。
原因は、js側でencodeURIComponent()しててphp側でdecodeurlしてたから。
どうしてかというと以下のサイトの表で判明しました。
http://www.programming-magic.com/file/20071227231138/urlencode.php
プラスが空白になってる!
結果を言うと以下のサイトにあるように
http://www.programming-magic.com/20071228223355/

PHP側はrawurlencode関数、JavaScript側はencodeURIComponent関数

を使えば解決です。
正直、URLエンコードは1種類だけしかないと思い込んでいたので、いい勉強になりました。

phpのヘッダーにも追加

iframeの中からサーバーに画像を送る時にもクロスオリジン制約がうるさく、受け手のphpの一番上に以下のソースを追加しないといけませんでした。

header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');

Ajaxのためだけに使用したライブラリー

上の画像を保存するphpにアクセスするためにAjaxを使いたく、以下のライブラリーを使用しました。
構造が簡単で使いやすかったと思います。
github.com

まとめ

初めて本格的にJSをいじって、超えるべき技術的な壁が多くて死にそうでした。
途中でコンソールの赤いエラーに何回も挫けそうになりました。
でもなんとか動くまでもっていけたので嬉しいのとホットしました。
JSの闇に足を踏み入れて行くことになりそうです。

どんなツイートにも画像を自動的に付けるwebサービス #pictureadd を作ったらヒットした

phpの技術を高めるためになにか作品を作ることになって、題材を考えてたら、Twitterの文章に関連した画像を張り付けてくれるようなサービスを作ったら面白いんじゃないかと思い立った。

http://pictureadd.matatsuna.net/


実装手順

1. Twitterにログイン
2. ツイートする文章を打ち込む
3. Yahoo!のテキスト解析にあるキーフレーズ抽出apiでキーワードを抜き出す
4. 関連度の高いキーワードで画像の検索を行う
5. 結果勝手にツイート

実装方法

他人のアカウントでOAuth認証して画像付きツイートする

記事を探してもこれと言ったものが見つからなかったので、前回使用したライブラリーの記事をよく探してみました。
前回の記事
ハッシュタグを固定して連続ツイートできる pinhashtag をphpで作った - matablo
そうしていたら以下の記事を見つけました。

ログインページを作る方法
qrbys.hatenablog.com

画像付きツイートをする方法
qiita.com

この二つを合わせれば行けるのではないのかと踏んでやってみたら、あっさりツイートできた。
(ツイートするところを少しいじるだけ)

キーワード抽出

何度もお世話になってるいつものapiです。
http://developer.yahoo.co.jp/webapi/jlp/keyphrase/v1/extract.htmldeveloper.yahoo.co.jp
phpからsimplexml_load_fileでxmlを読み込みました。

画像を検索する

phpでスクレーピングをしました。
初めのころは1つのキーワードで検索してましたが、現在は全てのキーワードで検索してます。
最近のアップデートで高速化を図りました。パースにライブラリーを使用することで10秒弱に短縮されました。

たくさんの反響





正直ここまで広く使われるとは思ってませんでした。
拡散した先がセキュリティ、競プロ、他大なので余計ハラハラドキドキで見守ってました。
最終的にはこのような感じになりました。
一気に使われて一気に収束しました。
ネットの拡散力の怖さを実感しました。
アクセス多すぎてazureの制限に引っかかってたけど、自分が作ったものを他人に使ってもらえることは嬉しい!

ツイートをまとめました!
togetter.com

今後の予定

この3つを夏を目標に開発して公開できたらいいなと思ってます。

===

変更・改良 (2018/08/17)

  • ヒットした画像が多かった場合はそのまま使用
  • 認証を全廃し、Twitter Cardを使用