先日の記事「Twitter API 愛の劇場のソース」では、Twitterのuser_timelineメソッドをPHPで呼び出し、simplexmlを使って処理しました。
しかし、記事中に書いてあるようにソースに不備があります。
書き直すに当たり、次の変更を行ないました。
- searchメソッドを使って検索する
- PHPを使わず、JavaScriptとJSONPだけで構成する。
前回のソースは、とりあえずTwitter APIがどんなものか動かしてみるレベルの内容です。
いいコードとはとてもいえないものでした。今回は使う開発言語を減らすことで、コードがスッキリしました。
アクセス制限の問題もあります。1時間に150アクセスまでなので、PHPを置いたサーバーからTwitterのサーバーにアクセスするより、JavaScriptを読み込んだ各ブラウザからTwitteのサーバーにアクセスさせる方がIPを分散できます。
さらに、前回はリレー小説の書き手分user_timelineメソッドを呼び出していました。今回はsearchメソッドで、Twitterのサーバーに問い合わせる回数を減らします。
search API
赤字のjsonをatomにすると、Atomが返ってきます。
http://search.twitter.com/search.json?q=検索クエリー&callback=コールバック関数名&result_type=recent&rpp=100&since_id=200851367760363520&page=1
必須パラメーターはqです。
q=には検索クエリーをURLエンコードして送信します。検索クエリーの書き方は後述します。
callback=にはJSONPのコールバック関数を指定します。このオプションはatomの時には適用されません。
result_type=には、欲しい検索結果の種類を指定します。指定できるのは以下の3つ。指定しないとrecentが指定されたものとみなされます。
recent 「新しい発言」を優先的に検索結果に含める
popular 「人気の高い発言」を優先的に検索結果に含める
mixed 「新しい発言」と「人気の高い発言」の両方を適度に検索結果に含める
rpp=には、一度に取得する検索結果の数を指定します。指定できる最大値は100、未指定だと15になります。
since_id=は指定したIDより大きなIDのステータスのみ取得するオプションです。検索に指定IDそのものは含まれません。
page=1ページを rpp 件とみなして取得するページを指定します。最初のページは1です。また、rppとpageで遡れるのは、最大1500件までです。
他にもたくさんのオプションがあり、WebService/Twitter/API - わすWikiにまとめられています。
ところが、今回も問題がありました。ある日から1話目が表示されなくなったのです。
どうも、約10日(1.5週)以上前のつぶやきは検索されないようです。
Twitter ビジネスユーザー向けブログ » Twitter APIで開発をするために知っておくべきことより
ということで、またスクリプトを修正することになるのですが、それは別の記事にして、この話を続けます。
検索クエリーの書き方
今回の検索クエリはこうなりました。
from:Tom3suteki で、投稿者がTom3sutekiのつぶやきを検索します。
リレー小説なので、投稿者が2人います。from:Tom3suteki または from:welcome2ourshop という場合は、ORで結びます。
ORは大文字で、前後に半角スペースを入れます。
検索から除外したい文字列の前には - (ハイフン)をつけます。ハイフンと文字列の間にはスペースを入れずにくっつけます。
-RT とすることでRTを含む文字を除外します。つまり、リツィートの除外を指定したことになります。
つづくなどと、何もつけない文字列を指定すると、その文字列を含んだツィートを探します。
つまり「from:Tom3suteki OR from:welcome2ourshop -RT つづく」は、「投稿者がTom3sutekiかwelcome2ourshopでRTを含まず(リツィートを含まず)、つづく という文字列があるツィートを探せ」という意味です。
これを、URLエンコードした上で、q=に続けて書きます。
ソースコード
以上をふまえて、書き直した「Twitterで愛の劇場」のソースコードが以下のものです。
<!-- 表示領域 --> <div id="twitter_novel_area" style="width:100%"></div> <script type="text/javascript"> var head=document.getElementsByTagName('head'); //専用スタールシートの読み込み var css_url='http://api.dwm.me/twitter/twitter_novel_viewer.css'; var css=document.createElement('link'); css.setAttribute('type','text/css'); css.setAttribute('rel','stylesheet'); css.setAttribute('href',css_url); head[0].appendChild(css); //JavaScriptの読み込み var js_url='http://api.dwm.me/twitter/twitter_novel_viewer.js'; var script=document.createElement('script'); script.setAttribute('type','text/javascript'); script.setAttribute('src',js_url); head[0].appendChild(script); </script>
読み込むJavaScript
//データ並び替え関数 詳細は「JavaScriptで表の並び替え」参照 var swap=function(id){ var table=document.getElementById(id); if(!table){return;} var tr=table.getElementsByTagName('tr'); var min=0,max=tr.length-1; while(min<max){ var td_min=tr[min].getElementsByTagName('td'); var td_max=tr[max].getElementsByTagName('td'); var tmp1=td_min[0].innerHTML; var tmp2=td_min[1].innerHTML; var tmp3=td_min[2].innerHTML; td_min[0].innerHTML=td_max[0].innerHTML; td_min[1].innerHTML=td_max[1].innerHTML; td_min[2].innerHTML=td_max[2].innerHTML; td_max[0].innerHTML=tmp1; td_max[1].innerHTML=tmp2; td_max[2].innerHTML=tmp3; min++; max--; } } var twitter_novel_viewer_properties=null; //コールバック関数 var twitter_novel_viewer_callback=function(json){ var results=json.results; var table=twitter_novel_viewer_properties.table; var url_base="https://twitter.com/#!/"; var url_search="http://search.twitter.com/search?q="; if(!table){return;} //テーブルにデータを挿入 for(var i=0;i<results.length;i++){ var tr=document.createElement('TR'); var td0=document.createElement('TD'); var td1=document.createElement('TD'); var td2=document.createElement('TD'); td1.innerHTML='<a href="' +url_base+results[i].from_user +'" target="_blank"><img src="' +results[i].profile_image_url +'"/></a>'; td2.innerHTML='<a href="' +url_base+results[i].from_user +'" target="_blank" class="name">' +decodeURIComponent(results[i].from_user_name) +'</a> <a href="'+url_base+results[i].from_user +'" target="_blank" class="screen_name">@' +results[i].from_user+'</a><br/>' +decodeURIComponent(results[i].text) .replace(/\\/g,'') .replace(/(https?:\/\/\S+)/g, '<a href="$1" target="_blank">$1</a>') .replace(/(#\S+)/g,function(arguments){ return '<a href="'+url_search +encodeURIComponent(arguments[1]) +'" target="_blank">' +arguments[1]+'</a>'; }) .replace(/@(\S+)/g,'<a href="' +url_base+'$1" target="_blank">@$1</a>') .replace(/つづく$/,'<em>つづく</em>'); tr.appendChild(td0); tr.appendChild(td1); tr.appendChild(td2); table.appendChild(tr); } //もし次のページがあったら、 //そのページを読み込んでページがなくなるまで再帰 if(json.next_page){ twitter_novel_viewer_properties.jsonp.parentNode .removeChild(twitter_novel_viewer_properties.jsonp); twitter_novel_viewer_properties.jsonp =document.createElement('script'); twitter_novel_viewer_properties .jsonp.setAttribute('type','text/javascript'); twitter_novel_viewer_properties .jsonp.setAttribute('src', twitter_novel_viewer_properties .rest_url+json.next_page +'&callback=twitter_novel_viewer_callback'); var head=document.getElementsByTagName('head'); head[0].appendChild(twitter_novel_viewer_properties.jsonp); }else{ //次のページがなくなったら、 //テーブルのいちばん左のカラムに番号を割り当て var tr=table.getElementsByTagName('tr'); for(var i=0;i<tr.length;i++){ tr[i].getElementsByTagName('td')[0] .innerHTML=tr.length-i; } } } function twitter_novel_viewer(properties){ if(!properties ||!properties.id ||!properties.rest_url ||!properties.query ||!properties.callback){return null;} var elem=document.getElementById(properties.id); var head=document.getElementsByTagName('head'); if(!elem||!head.length){return null;} //検索クエリー作成 twitter_novel_viewer_properties ={'page':0,'rpp':15,'rest_url':properties.rest_url}; twitter_novel_viewer_properties.rest=properties.rest_url +'?q='+encodeURIComponent(properties.query) +'&callback='+properties.callback; if(properties.rpp){ twitter_novel_viewer_properties.rpp=properties.rpp; twitter_novel_viewer_properties.rest +='&rpp='+properties.rpp; } if(properties.result_type &&properties.result_type.match(/^(recent|popular|mixed)$/)){ twitter_novel_viewer_properties.rest +='&result_type='+properties.result_type; } if(properties.sincs_id&&properties.since_id.match(/^[0-9]{18}$/)){ twitter_novel_viewer_properties.rest +='&since_id='+properties.since_id; } //書き込み用表テーブルの<ベースを作成 var table=document.createElement('TABLE'); var tbody=document.createElement('TBODY'); var caption=document.createElement('CAPTION'); table.setAttribute('id','twitter_novel_viewer'); table.setAttribute('style','width:100%'); caption.setAttribute('style','width:100%;text-align:center'); caption.innerHTML ='Twitter愛のリレー小説<br/>' +'<span>「セルヴーズの雨傘」</span>'; table.appendChild(caption); table.appendChild(tbody); elem.innerHTML='<p style="text-align:right">' +'<input type="button" value="古いのを上にする" ' +'onclick="swap(\'twitter_novel_viewer\');' +'this.value=this.value==\'古いのを上にする\'' +'?\'新しいのを上にする\':\'古いのを上にする\';"/>' +'</p>'; elem.appendChild(table); twitter_novel_viewer_properties.table=tbody; //JSONPを要求するためヘッダにscriptタグ挿入 twitter_novel_viewer_properties.jsonp =document.createElement('script'); twitter_novel_viewer_properties .jsonp.setAttribute('type','text/javascript'); twitter_novel_viewer_properties .jsonp.setAttribute('src', twitter_novel_viewer_properties.rest+'&page=1'); head[0].appendChild(twitter_novel_viewer_properties.jsonp); } //クエリ作成用データ var properties={ 'id':'twitter_novel_area', 'rest_url':'http://search.twitter.com/search.json', 'query':'from:Tom3suteki OR from:welcome2ourshop -RT つづく', 'callback':'twitter_novel_viewer_callback', 'rpp':100, 'result_type':'recent', 'since_id':'200851367760363520', 'css_href':'http://in/twitter_novel_viewer.css' } twitter_novel_viewer(properties);
上記ソースを実際に適用したのが「Twitterで愛の劇場」です。