C#でtwitterizerをつかってみた3 TwtterAPIのアクセス回数を削減する方法[.NET][C#][twitterizer][twitterAPI][Tips]
■TwitterAPIの制限
TwitterAPIには「1時間に150回まで」の制限があります。
なので、定期的につぶやくbotなどではそこまで困りませんが、
ちょっと工夫して、ユーザ画像や過去のつぶやき、フォロワー、フォローしている人(のことを英語ではFriendと言うらしい。一方的な関係なのになんか変…)の情報を取得しているうちに、すぐ上限に達してしまったりします。
前回の日記では、そういう工夫はせずに、書いていましたが、
今回は、これをできるだけ避けるような、アクセス回数を節約する方法を実装してみました。
■制限の節約方法
解決法は、まず誰でも思い付きそうなのが、「一度取った情報は退避する」てことです。
文章で説明するよりも、図で説明した方が早いでしょう。
ようするに、こういうことです。
ご利用は計画的に、てことですね。
今回は、一番よく使われると思われる、ツイッターのユーザ情報もろもろを取得するTwitterUser.Show()メソッドだけに注目して、Dictionaryクラスに格納してみます。
なお、ユーザ情報は10進数のId(_twitterUser.Id、Decimal型)から取得されるか、
もしくはTwitterID(_twitterUser.Name、string型)から取得されるかわからないので、
ちょっとメモリ的には冗長ですが、両方用意しました。
こうすることで、同じユーザ情報を取得する場合は、どちらから先に呼びだされても、
API呼びだし回数を1回に抑えることができます。
具体的には、一度呼びだしたユーザ情報TwitterUserクラスのインスタンス(_twitterUser)を、Dictionary[Key=TwitterID][Value=_twitterUser]
Dictionary[Key=10進数のId][Value=_twitterUser]
に格納しました。
using System; using System.Collections.Generic; using System.Text; // Twitterizerライブラリを使っている using Twitterizer; // Windows.Forms依存にしたくない時はコメントアウト+修正 using System.Windows.Forms; using System.Drawing; using System.Net; using System.IO; namespace everyoneCanCreate.Twitter { public class MyTwitterTools { //… //他の処理 //… #region ユーザ情報などを辞書に登録する処理: getTwitterUser()など、ほとんどのメソッドで共通して使われる処理なので、TwitterAPIアクセス回数を節約するために、少し工夫している /// <summary> /// (1時間に150アクセスまでという)TwitterAPIアクセス回数節約のために、一度読み込んだデータを辞書に格納して使うか。 /// trueだと処理はほんの少し重たくなるかもしれないが節約。falseだと余計なことはせずAPIをじゃんじゃん使う。 /// /// ※現時点では、trueにしないとすぐ1時間150回をすぐ使いきっちゃうので、trueにしている。 /// </summary> public static bool p_isUseDictionary・TwitterAPIアクセス回数削減のために辞書を使うか = true; /// <summary> /// 一度getTwitterUser・ユーザ情報を取得(@からはじまるtwitterID)で読み込んだTwitterUserクラスのインスタンスを格納している辞書です。 /// /// (1時間に150アクセスまでという)TwitterAPI実行数節約のために、一度読み込んだユーザのデータを取得するときに使用します。 /// </summary> private static Dictionary<string, TwitterUser> p_Users_KeyTwitterID・今まで取得したユーザの辞書 = new Dictionary<string, TwitterUser>(); /// <summary> /// 一度getTwitterUser・ユーザ情報を取得(TwitterUser.Idのユーザ識別子)で読み込んだTwitterUserクラスのインスタンスを格納している辞書です。 /// /// (1時間に150アクセスまでという)TwitterAPI実行数節約のために、一度読み込んだユーザのデータを取得するときに使用します。 /// </summary> private static Dictionary<decimal, TwitterUser> p_Users_KeyDecimalId・今まで取得したユーザの辞書 = new Dictionary<decimal, TwitterUser>(); /// <summary> /// ツイッターID(@で始まる英数字記号の文字列)からユーザ情報TwitterUser型のインスタンスを取得します。失敗した場合は、nullが入ります。 /// /// ※このクラス内では、twitterAPI実行数を節約するため、getUser・ユーザ情報を取得(..)の代わりに呼びだしています。 /// </summary> /// <returns></returns> public static TwitterUser getUser・ユーザ情報を取得(string _twitterID) { if (p_isUseDictionary・TwitterAPIアクセス回数削減のために辞書を使うか == true) { // APIを呼びだす前に、既に辞書に格納されていないかを確認 if (p_Users_KeyTwitterID・今まで取得したユーザの辞書.ContainsKey(_twitterID) == true) { // これでAPI呼びだす回数を節約できる。 return p_Users_KeyTwitterID・今まで取得したユーザの辞書[_twitterID]; } } // 普通にAPIを呼びだして取得 TwitterUser _user = null; TwitterResponse<TwitterUser> _res = TwitterUser.Show(_twitterID); // これでも150回のTwitterAPIアクセス回数は消費するから注意して! if (_res.Result == RequestResult.Success) { _user = _res.ResponseObject; } else if (_res.Result == RequestResult.Unknown) { checkError・エラー処理("TwitterID「" + _twitterID + "」のユーザが見つかりませんでした。", _res.ErrorMessage); } else { checkError・エラー処理("ツイッターIDからユーザ情報(TwitterUser)取得時のエラーです、", _res.ErrorMessage); } // 一度取得したユーザ情報を辞書に登録(辞書を使う場合だけ) if (p_isUseDictionary・TwitterAPIアクセス回数削減のために辞書を使うか == true) { if (_user != null) { // それぞれの辞書に、登録されていなければ、新しくユーザ情報を取得 // こちらは確実に登録されていない p_Users_KeyTwitterID・今まで取得したユーザの辞書.Add(_twitterID, _user); // こちらは登録されているかわからないので確認 decimal _decimalId = _user.Id; if (p_Users_KeyDecimalId・今まで取得したユーザの辞書.ContainsKey(_decimalId) == false) { p_Users_KeyDecimalId・今まで取得したユーザの辞書.Add(_decimalId, _user); } } } return _user; } /// <summary> /// TwitterUser.Idのユーザ識別子(Decimal型のユーザ識別子)からユーザ情報TwitterUser型のインスタンスを取得します。失敗した場合は、nullが入ります。 /// /// ※このクラス内では、twitterAPI実行数を節約するため、getUser・ユーザ情報を取得(..)の代わりに呼びだしています。 /// </summary> /// <returns></returns> public static TwitterUser getUser・ユーザ情報を取得(decimal _TwitterUser_Id_decimal) { if (p_isUseDictionary・TwitterAPIアクセス回数削減のために辞書を使うか == true) { // APIを呼びだす前に、既に辞書に格納されていないかを確認 if (p_Users_KeyDecimalId・今まで取得したユーザの辞書.ContainsKey(_TwitterUser_Id_decimal) == true) { // これでAPI呼びだす回数を節約できる。 return p_Users_KeyDecimalId・今まで取得したユーザの辞書[_TwitterUser_Id_decimal]; } } // 普通にAPIを呼びだして取得 TwitterUser _user = null; TwitterResponse<TwitterUser> _res = TwitterUser.Show(_TwitterUser_Id_decimal); if (_res.Result == RequestResult.Success) { _user = _res.ResponseObject; return _user; } else if (_res.Result == RequestResult.Unknown) { checkError・エラー処理("Decimal型のユーザ識別子「" + _TwitterUser_Id_decimal + "」のユーザが見つかりませんでした。", _res.ErrorMessage); } else { checkError・エラー処理("ユーザ識別子からユーザ識別子取得時のエラーです、", _res.ErrorMessage); } // 一度取得したユーザ情報を辞書に登録(辞書を使う場合だけ) if (p_isUseDictionary・TwitterAPIアクセス回数削減のために辞書を使うか == true) { if (_user != null) { // それぞれの辞書に、登録されていなければ、新しくユーザ情報を取得 // こちらは確実に登録されていない p_Users_KeyDecimalId・今まで取得したユーザの辞書.Add(_TwitterUser_Id_decimal, _user); // こちらは登録されているかわからないので確認 string _twitterID = _user.Name; if (p_Users_KeyTwitterID・今まで取得したユーザの辞書.ContainsKey(_twitterID) == false) { p_Users_KeyTwitterID・今まで取得したユーザの辞書.Add(_twitterID, _user); } } } return null; } #endregion /// <summary> /// このクラスのエラー処理を共通して管理するメソッドです。 /// /// TwitterResponse.Result != Success だったとき、一貫してこのメソッドを呼び出してください。 /// そうすると、エラー処理の個所をひとつにまとめられます。 /// </summary> /// <param name="_ShownMessage・表示したいメッセージ"></param> /// <param name="_details・詳細エラーメッセージ_TwitterResponse_ErrorMessage"></param> /// <returns></returns> public static bool checkError・エラー処理(string _ShownMessage・表示したいメッセージ, string _details・詳細エラーメッセージ_TwitterResponse_ErrorMessage) { MessageBox.Show("【エラー】" + _ShownMessage・表示したいメッセージ + "\n\n詳細:" + _details・詳細エラーメッセージ_TwitterResponse_ErrorMessage, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); // 以下、エラーの内容によって、よくはまる事例をまとめてくれているサイト。感謝。 http://oresta.blog66.fc2.com/blog-entry-22.html // よくある、"Status is a duplicate."エラーは重複。Twitterでは同じ発言を繰り返し投稿できません。少なくとも10ツイートはあける必要があります。 http://www26.atwiki.jp/easybotter_wiki/pages/21.html return true; }
以上です。
■使用例
このメソッドを使えば、例えば、同じユーザ情報を150回取得しても、
TwitterAPIアクセスが止められることはありません。
普通のTwitterUser.Show()を呼びだした時と比べてみます。
public static void HELP・このクラスの使い方のヘルプ() { string _message = "テストのつぶやき"; string _info = ""; MyTwitterTools.tweet・つぶやく(_message); System.Console.WriteLine(_info); // 同じユーザ情報を150回ずつ取得 string _twitterID = "merusaia"; // テストする時は、5000以下のフォロワーの人にしようね TwitterUser _user; // (a)節約する場合 for (int i = 0; i < 151; i++) // 問題無く、ポケモンゲットだぜ! { _user = MyTwitterTools.getUser・ユーザ情報を取得(_twitterID); _info = i + "回目のMyTwitterTools.getUser・ユーザ情報を取得(_twitterID)"; System.Console.WriteLine(_info); } // ただし、p_isUseDictionaryをtrueにしてたらだからね。falseにしてたら(b)と一緒だよ // (b)節約しない場合 var _res = new TwitterResponse<TwitterUser>(); for (int i = 0; i < 151; i++) // 問題ありあり、アクセス禁止ゲットだぜ! { _res = TwitterUser.Show(_twitterID); _info = i + "回目のTwitterUser.Show(_twitterID)"; System.Console.WriteLine(_info); // どっかで止まる。 } // TwitterAPIの制限(1時間に150回)の1回には、TwitterUser.Showが含まれている // Rate limit exceed.が出たら1時間、実験できないからね。実行する時は気を付けて。 System.Windows.Forms.Application.Exit(); } ※はてなダイアリーでは、ソースの色付き引用は、コピペしたソースを「>|cs|」〜「||<」で囲むだけでいいんですね。いやぁ、便利な機能ですね。 (a)節約する場合では、1時間に何回実行してもポケモンゲッ…ユーザ情報をゲットできますが、 (B)節約しない場合では、必ずアクセス上限(Rate limit exceed.から始まるメッセ―ジ) が表示されます。 この他にも、例えばフォロワーを取得するTwitterFriendShip.Show()を呼び出す時に、内部にgetUserを使えば、二回目以降にフォロワーを取る場合は、使い回しができたりします。 フォロワーを意識したbotを作りたい人は、試してみるといいかもです。 (ただし、リアルタイムなフォロワーの更新や通知はできませんが。Stream系クラスを使えばできるらしいです。) ツイッタークライアント等を作っているもっと詳しい方には、「そんなのStreamを使えばいいじゃん」とか言われそうですが、 Streaming系のクラスは結構使い方がややこしく、またAPI仕様変更も頻繁だと聞くので、 (仕様変更が怖くて)あまり手を出せていません^^;。 できれば、開発者にもTwitterAPIにも優しい、地球にやさしいプログラムを作っていきたいものですね。 ■MyTwitterTools.cs なお、これを意識して、twitterizerを日本語風にわかりやすく(?)ラッピングしたクラス「MyTwitterTools.cs」、だいぶ変わりました。 ソース全文をダウンロードしたい方は以下からお気軽にどうぞ。 ■dropbox.com/home/Public/minRPG_みんつくプロジェクト https://www.dropbox.com/home/Public/minRPG_%E3%81%BF%E3%82%93%E3%81%A4%E3%81%8F%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88 (※VisualStudio2010用なので、文字コードはUTF-8です)。 上記URL → ソースコード → 最新日付 → 「***.zip」をダウンロード&解凍 → その中の「minRPG/_NET4_0Program_Twitterアプリ/NET4_0_Toolsプログラミングツール系」 というフォルダの中に、「MyTwitterTools.cs」が入っています。(.NET Framework4.0が必要) なお、動かしたい場合は「MyTools.cs」というファイルも必要です。(このファイルのメソッドを使っているため) あとはプロジェクトに、twitterizer***.dllをNuGetを使って、自動的に参照するだけですね。 「twitterizerてなによ?」「NuGetって何?」て方は、この記事などを参考にしてください。 (NuGet、便利ですよね。。私も最近知りました…><) http://mimumimu.net/blog/2011/11/22/nuget-%E3%81%A7%E7%B0%A1%E5%8D%98%E3%81%AB%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B%E3%80%82/ ※ここの内容は、全てパブリックドメインとし、著作権を放棄します。ご自由にコピー・改変してお使いください。