PHP4の環境でTwitter API1.1対応した時の備忘録です。
現状
http://api.twitter.com/1/statuses/followers.format
- ↑APIを使ってアカウント毎(都道府県の)に、フォローワーを取得
- ユーザーのscreen_nameが有るかチェック
- screen_nameが有ったら特定の処理
という事をやっていました。
今後
まずはoauth対応が必要
developerサイトでコンシューマ登録(Consumer key、Consumer secret、Access token、Access token secret取得)し、oauthが使えるようにする。
自分のアカウント情報わかればいいので、コールバックは設定なし(developerサイトで取得したAccess tokenのみでOK)
どのAPIを使うか
もともと、statuses/followers.formatは非推奨だったようで且つAPI1.1のドキュメントには使えると書いてないし、
API1のドキュメントにも GET followers/ids and GET users/lookup. 使ってねって書いてある。
https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi
↑からid一覧が取得できるのでそこから
https://api.twitter.com/1.1/users/lookup.json?screen_name=twitterapi,twitter
↑を使って、screen_nameを取得する。
※100ユーザーまで一度に調べれる(カンマ区切りでパラメーター)
疑問
APIのリクエストリミット制限に引っかかるのでは?
- 今までのリクエストリミット
- OAuthなしの場合はIPアドレス当たり1時間150回、OAuthありの場合は1アカウント当たり1時間350回
- 新しいAPIのリクエストリミット
- ・API 1.1では、1アカウント当たり15分で“エンドポイントごとに(API毎に)”15回または180回
https://dev.twitter.com/docs/api/1.1/get/application/rate_limit_status
↑で各エンドポイント毎のリミッター分かる。
結局
制限は仕方ないと割りきって、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得する。
users/lookupはパラメーターが長くなるのでPOSTがよさそう。
OAuthのパーミッションがRead-only だとPOST出来ないよ。
Utilクラス
<?php require_once 'HTTP/Request.php'; require_once 'Jsphon/Decoder.php'; require_once 'twitter_config.php'; error_reporting(-1); define('REQUEST_DOMAIN', 'https://api.twitter.com'); class TwitterUtil { // --------------------------------------------- // friendships/lookup // --------------------------------------------- function friendshipsLookup($follow_account, $screen_name) { if (!isset($follow_account) || !isset($screen_name)) { return; } // APIパラメーター $endPointParam = array('screen_name' => $screen_name); // アカウント情報取得 $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account]; // 投げる変数類設定 $requestParam = $this->_initOAuthParam($accountConfig); $requestParam += $endPointParam; // Signatureベーステキストの作成 $requestUri = REQUEST_DOMAIN . '/1.1/friendships/lookup.json'; $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET); // 最初に定義したパラメータリストからscreen_name撤去 unset($requestParam['screen_name']); // Signatureの作成 $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']); $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase)); // リクエスト $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam); return $result; } // --------------------------------------------- // followers/ids // --------------------------------------------- function followersIds($follow_account, $cursor) { if (!isset($follow_account) || !isset($cursor)) { return; } // APIパラメーター $endPointParam = array('cursor' => $cursor, 'screen_name' => $follow_account,); // アカウント情報取得 $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account]; // 投げる変数類設定 $requestParam = $this->_initOAuthParam($accountConfig); $requestParam += $endPointParam; // Signatureベーステキストの作成 $requestUri = REQUEST_DOMAIN . '/1.1/followers/ids.json'; $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET); // 最初に定義したパラメータリストからcursor,screen_name撤去 unset($requestParam['cursor'], $requestParam['screen_name']); // Signatureの作成 $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']); $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase)); // リクエスト $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam); return $result; } // --------------------------------------------- // users/lookup // --------------------------------------------- function usersLookup($follow_account, $user_id ) { if (!isset($follow_account) || !isset($user_id)) { return; } // APIパラメーター $endPointParam = array('user_id' => $user_id); // アカウント情報取得 $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account]; // 投げる変数類設定 $requestParam = $this->_initOAuthParam($accountConfig); $requestParam += $endPointParam; // Signatureベーステキストの作成 $requestUri = REQUEST_DOMAIN . '/1.1/users/lookup.json'; $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST); // 最初に定義したパラメータリストからscreen_name撤去 unset($requestParam['user_id']); // Signatureの作成 $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']); $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase)); // リクエスト $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_POST, $endPointParam); return $result; } // --------------------------------------------- // application/rate_limit_status // --------------------------------------------- function status($follow_account, $resources) { if (!isset($follow_account) || !isset($resources)) { return; } // APIパラメーター $endPointParam = array('resources' => $resources); // アカウント情報取得 $accountConfig = $GLOBALS['TWITTER_CONFIG']['ACCOUNT'][$follow_account]; // 投げる変数類設定 $requestParam = $this->_initOAuthParam($accountConfig); $requestParam += $endPointParam; // Signatureベーステキストの作成 $requestUri = REQUEST_DOMAIN . '/1.1/application/rate_limit_status.json'; $signatureBase = $this->_createSignatureBase($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET); // 最初に定義したパラメータリストからresources撤去 unset($requestParam['resources']); // Signatureの作成 $keys = $this->_rawurlencode($accountConfig['CONSUMER_SECRET']) . '&' . $this->_rawurlencode($accountConfig['ACCESS_TOKEN_SECRET']); $requestParam['oauth_signature'] = base64_encode($this->_hmacsha1($keys, $signatureBase)); // リクエスト $result = $this->_authRequest($requestUri, $requestParam, HTTP_REQUEST_METHOD_GET, $endPointParam); return $result; } // --------------------------------------------- // リクエスト(GET,POST) // --------------------------------------------- function _authRequest($uri, $param, $method, $addData) { // HTTPヘッダーに追加するAuthorizationパラメータ作成 $requestHeaders = array(); foreach ($param as $k => $v) { $requestHeaders[] = $k . '="' . $this->_rawurlencode($v) . '"'; } $requestHeader = 'OAuth ' . implode(', ', $requestHeaders); // PEARのRequestを使ってポスト $request = &new HTTP_Request($uri); $request->setMethod($method); $request->addHeader('Authorization', $requestHeader); if ($method == HTTP_REQUEST_METHOD_GET) { foreach ($addData as $k => $v) { $request->addQueryString($k, $v); } } else { foreach ($addData as $k => $v) { $request->addPostData($k, $v); } } $result = $request->sendRequest(); if (PEAR::isError($result)) { return; } $json = new Jsphon_Decoder(); return $json->decode($request->getResponseBody()); } // --------------------------------------------- // OAuth認証共通パラメーター初期化 // --------------------------------------------- function _initOAuthParam($config) { return array( 'oauth_consumer_key' => $config['CONSUMER_KEY'], 'oauth_nonce' => md5(uniqid(mt_rand(), TRUE)), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_token' => $config['ACCESS_TOKEN'], 'oauth_version' => '1.0', ); } // --------------------------------------------- // Signatureベーステキスト作成 // --------------------------------------------- function _createSignatureBase($uri, $param, $method = 'GET') { if (!isset($uri)) { return; } // 変数名でソート ksort($param); // Sigunatureのベーステキストを作成 $queries = array(); foreach ($param as $k => $v) { $queries[] = $k . '=' . $this->_rawurlencode($v); } $query = implode('&', $queries); // それぞれをURLエンコードして接続 $signatureBase = "{$method}&"; $signatureBase .= $this->_rawurlencode($uri) . '&'; $signatureBase .= $this->_rawurlencode($query); return $signatureBase; } // --------------------------------------------- // URLエンコード RFC3986版 // --------------------------------------------- function _rawurlencode($str) { $str = rawurlencode($str); $str = str_replace('+', ' ', $str); $str = str_replace('%7E', '~', $str); return $str; } // --------------------------------------------- // hash_hmac('sha1', $data, $key, true)の代わり // --------------------------------------------- function _hmacsha1($key, $data) { $blocksize = 64; $hashfunc = 'sha1'; if (strlen($key) > $blocksize) { $key = pack('H*', $hashfunc($key)); } $key = str_pad($key, $blocksize, chr(0x00)); $ipad = str_repeat(chr(0x36), $blocksize); $opad = str_repeat(chr(0x5c), $blocksize); $hmac = pack('H*', $hashfunc(($key ^ $opad) . pack('H*', $hashfunc(($key ^ $ipad) . $data)))); return $hmac; } }
PEARのHTTP_REQUESTを使ってリクエストを送って、jsonはJsphonを使ってデコードしています。
PHP4の環境なので
PHP4でoAuth。Twitter APIでつぶやく。「マチルダさ~ん」 | motooLogue.
を参考に作りました。
twitter_config.phpには
<?php $GLOBALS['TWITTER_CONFIG']['ACCOUNT']['aaa'] = array( 'CONSUMER_KEY' => 'asdfasdfeii83jeoasdfasd', 'CONSUMER_SECRET' => 'asdfase90ia3aksjdflkasdlfasdfasdfas', 'ACCESS_TOKEN' => '12103981-asdfasdJOE+Spassaspdoep', 'ACCESS_TOKEN_SECRET' => 'asdfDFIELKJF38293lsdsf89DKKKLL' );
のような感じでアカウント情報を保持するようにしました。
呼び出し元は
<?php require_once 'twitter_util.php'; $twitterUtil = new TwitterUtil(); $data = $twitterUtil->followersIds('aaa', '-1'); echo '<pre>'; print_r($data); echo '</pre>'; exit;
のようにリクエスト送信、レスポンス取得ができるので、各アカウントでfollowers/ids(リミット:15回/15分)でフォローされているidを取得して、users/lookup(リミット:180回/15分)でscreen_nameを取得、チェックして特定の処理を行えば対応完了となりました。
PHP4用のOAuth対応ブログが大変参考になりました。