Twitter APIを使ってツイート数を数える—ソーシャルボタンをJavascriptフリー・PHPで実装しよう!(4)

ツイッター以外のソーシャルボタンをPHPで実装した記事から随分経ってしまいましたが、少なくとも日本ではツイッターが一番シェアされやすいソーシャルメディアです。私も、ツイッターのシェア数が高い記事は何となくじっくり読もうと感じますので、ぜひシェア数を載せたいところです。

Sponsored link

ツイッターのシェア数取得が難しく

ツイッターのシェア数は、2015年秋にツイッター社の方針転換により取得できなくなりました。大手メディアなども一斉にツイートボタンにシェア数を載せなくなって、ツイート数を目にする機会は減っています。

2015年11月のcount.json廃止以降もツイート数を表示させる方法はありますが、だいぶ面倒になりました。まずは、正攻法でツイッターの公式機能を使って実装していきます。この方法では、PHPだけでも表示させられますが時間帯によってはかなり表示遅延が起こります。そのため、最終的にはJavascriptを併用してサイトを公開しました。

ページを作ってから時間がたってしまったので、だいぶ忘れてしまいましたが、ここではPHP(とデータベース)を使ってツイート数を取得する方法を書いていきます。

2015年秋以降、ツイッターシェア数を得る方法

ツイッターの非公開APIが停止して以来、ツイッターでのシェア数を得る方法は大きく分けて2種類あります。

個人で使える可能性があるのは、ツイッターの公開インターフェイス(API)を使用する方法です。以前の非公開APIと比べると使い勝手がだいぶ悪く、正直面倒です。何が悪いかというと、15分毎に180回(認証方式によっては450回)という使用制限がかかっています。後で書きますが、ページ数が多いブログサイトではかなり困難が予想される数字です。さらに、1週間までの記録しかたどれません。

もう一つは、ひたすらすべてのツイートを調べていく方法で、おそらくツイート数を収集しているサイト(日本のものではなくアメリカの、~~ビリオンツイート回収済み!と言っているところ)ではこちらの方法なのかなと思います。

Twitter API

TwitterのAPIは、Twittterのアカウントを作って「アプリ」を作成し、登録した認証情報を使ってアクセストークンを得てTwitter社のAPIへcurlでアクセスします。

上に書いているように、15分間に450回までという制限があり、さらに1週間分しかたどれないのが難点です。サイトが大規模でなく、検索流入を得ているページが少ないウェブサイトなら良いですが、多くのページにアクセスを受けるサイトの場合には、アクセスを受けた際にTwitterAPIを呼び出さないという分岐を作っておかないとすぐにパンクしてしまいます。

Twitter APIのコード自体は、開発者向けページに公開されているコードをほとんど流用することで何とかなります。OAuthの認証コードがちょっと面倒で、短期間で破棄することと書かれていますが、有効期限は設定されておらず1アプリに対して1つしかコードが発行されません。そのため、最初はTwitter APIの呼び出しと同時に(古くなっている場合)認証コードの破棄をするようにしてみたのですがうまくいきませんでした。

ツイート数をカウントするためのPHPプログラムは、Searchを繰り返して結果を保存するというものになります。データベースもしくはファイルベースでのデータ保存が必要になります。保存するデータは、ツイート用のURL、過去のツイート数、最後にAPI問い合わせをした時の最後のツイートID、最後にAPI問い合わせをした時のタイムスタンプ、です。

APIの使用制限のため、タイムスタンプを残しておいてAPIを使うかどうかを判断します。また、ツイートIDを保存しておくことで重複を防ぎます。が、1週間以内に呼ばれない場合はツイートされていてもカウントされません。

私は最終的に、フェイスブックのシェア数取得APIが遅くて(3秒以上かかることも・・・)ページ表示に支障が出たため、AJAXを使ったJavascriptを結局使ってウェブサイトを作りました。AJAXを使うとページが表示された後にバックグラウンドで動作できるようになります。ただし、AJAXを使った場合にはページが同時に表示されたときやページキャッシュの表示に問題が出てしまうので、同時にアクセスされたときにできるだけAPIが叩かれる回数を減らし、ツイート数の動的更新もあきらめました。

大体こんな感じに。ツイートIDがPHP_INT_MAXを超えたらトラブる予定なので、いつか何とかしないといけません。取り合えず、データベース上のデータはSTRING型として保存しておきましょう。

$url = "https://api.twitter.com/1.1/search/tweets.json"; // base url
$num_tweet = 15;
$formed_url ='?q='.urlencode(trim($URL)).'&result_type=recent&include_entities=false&count='.$num_tweet . '&since_id=' .$prev_twitter_lastid;
//$formed_url ='?q='.urlencode("https://www.google.co.jp/").'&count='.$num_tweet.'&result_type=recent&include_entities=false&since_id=' .$prev_twitter_lastid; //&max_id=700381650757091328';

//"max_id" can be included in the response. (should be equal or less)
//"since_id" will not be included in the reponse. (should be more)
$headers = array( 
  "GET /1.1/search/tweets.json".$formed_url." HTTP/1.1", 
  "Host: api.twitter.com", 
  "User-Agent: jonhurlock Twitter Application-only OAuth App v.1",
  "Authorization: Bearer ".$tokens{0}->token, //to be changed in PHP 5.6+
);
$ch["searchTweets"] = curl_init();  // setup a curl
.....
while(true)
{
  $count_tw_found += count($searchresult["statuses"]);
    if(!array_key_exists("statuses",$searchresult) || count($searchresult["statuses"])<$num_tweet-1)
    	break;
    $url = "https://api.twitter.com/1.1/search/tweets.json"; // base url

  //get least id_str of obtained tweets.
    //max_id in next request will be $max_id - 1;
  $max_id = $searchresult["statuses"][ count($searchresult["statuses"])-1]["id_str"];
    //it may exceed PHP_INT_MAX
    while(!empty($max_id))
    {
     	$max_id_part[] = substr($max_id, 1-strlen((string)PHP_INT_MAX));
    if(strlen($max_id)-strlen((string)PHP_INT_MAX)<0) break;
    $max_id = substr($max_id, 0, strlen($max_id)-strlen((string)PHP_INT_MAX)+1);   
  }
    for($i=0; $i < count($max_id_part); ++$i)
    {
    	$deduct = (string)((int)$max_id_part[$i] - 1);
        if($deduct=="-1")
        {
        	$max_id_part[$i] = sprintf("%'9".(strlen((string)PHP_INT_MAX)-1)."d", 9);
        	continue;
        }
        $max_id_part[$i] = $deduct; break;
    }
    $max_id = $max_id_part[count($max_id_part)-1];
    if($max_id == 0) $max_id="";
    for($i=count($max_id_part)-2; $i >= 0; --$i)
    	$max_id .= sprintf("%0".(strlen((string)12345)-1)."d", $max_id_part[$i]);
    unset($max_id_part);
    //$max_id should be since_id of next request.
        
  $formed_url ='?q='.urlencode(trim($URL)).'&result_type=recent&include_entities=false&count='.$num_tweet.'&max_id='.$max_id .'&since_id=' .$prev_twitter_lastid;
  //$formed_url ='?q='.urlencode("https://www.google.co.jp/").'&count='.$num_tweet.'&result_type=recent&include_entities=false&max_id='.$max_id .'&since_id=' .$prev_twitter_lastid;
  $headers = array( 
  "GET /1.1/search/tweets.json".$formed_url." HTTP/1.1", 
  "Host: api.twitter.com", 
  "User-Agent: jonhurlock Twitter Application-only OAuth App v.1",
  "Authorization: Bearer ".$tokens{0}->token, //to be changed in PHP 5.6+
  );
  $re_ch = curl_init();  // setup a curl
  curl_setopt($re_ch, CURLOPT_URL,$url.$formed_url);  // set url to send to
  curl_setopt($re_ch, CURLOPT_HTTPHEADER, $headers); // set custom headers
  curl_setopt($re_ch, CURLOPT_RETURNTRANSFER, true); // return output
  unset($searchresult);
    $searchresult=json_decode(curl_exec($re_ch),true);
  curl_close($re_ch);
}

使っているコードを一部切り出しているので、このままでは1000%動きませんが、なんとなく自分の覚えのために・・・。効率とか考えられる能力はないのでたぶん非効率ですが、結果が15件まで検索されるので、もし15件だった場合には検索するツイートIDを最後に検索されたものに変えてもう一回検索します。

count.jsoon

最後に作ったサイトではPHPとMySQL、Javascriptで自作したのですが、結構面倒なうえに取りこぼしも多いはずです。以前のcount.jsonのように使えるAPIをIT企業のディジティミニミが運営してくださっているので、そちらを使うほうが汎用性が高くお勧めです。

count.jsoon

ただし、無料サービスの常として、有料化の可能性が常にあることと、利用者が増えた場合はオーバーフローして破たんする可能性もあることを念頭に、大事なサイトなら自分でcount.jsoonからのカウント数をデータベースに保存するなどしておいたほうが安心です。

でも、ディジティミニミの代表者の竹中直純氏は日本のインターネット黎明期から活躍されているプログラマーだそうですので、大丈夫かもしれません。

サイト用に作ったTwitterAPIへのアクセス権を譲渡する必要がありますが、過去の投稿も含めてクロールしてくれるので、非常に便利です。

count.jsoonの代替サービス

十分に使い勝手がよいcount.jsoonですが、常にオプションを持っていたいので、別のサービスも見ておきます。

英語圏では多数のサービスがすでに立ち上がっています。(有料のところも・・)

(実際に試していないので、動くかは知りません。。試す機会がないことを祈ります)

Sponsored link
Spinsored link

シェアする

  • このエントリーをはてなブックマークに追加

フォローする