ラベル Twitter の投稿を表示しています。 すべての投稿を表示
ラベル Twitter の投稿を表示しています。 すべての投稿を表示

2012/07/23

文字実体参照を記号(文字)に置き換える

JSONやXMLデータを表示するクライアントを作った時などに、文字実体参照になっている記号がそのまま & とか表示されたりする。
パーサがお利口な場合はよしなに記号に置き換えてくれたりするけど、そうじゃない場合もあるので、その場合は自前で文字列置換を行ってあげる必要がある。

自前のアプリでは今のところ下記について対応していて、必要があれば適宜追加していっている。

""
""
&&
&&
''
''
&lt;<
&#60;<
&gt;>
&#62;>

てなことを &amp; が表示されちゃってるTwitterアプリを見て思ったりしたw

2011/01/19

TwitterとGoogle App Engineの自分用Webアプリケーション「TwitMail」(ソース)

先月つくったTwitMail(TwitterとGoogle App Engineの自分用Webアプリケーション)、未だTaskQueueの実装が済んでいないですが、現状のもののソースを公開しておきます。

app.yaml
application: アプリ名
version: 1

runtime: python
api_version: 1

handlers:
- url: /main
  script: get_timeline.py

- url: /send
  script: send_tweet.py

- url: /db/delete
  script: db_delete.py

- url: .*
  script: default.py

cron.yaml
cron:

- description: Get Timeline job
  url: /main
  schedule: every 10 minutes

- description: Send Tweet job
  url: /send
  schedule: every 15 minutes

- description: DB Delete job
  url: /db/delete
  schedule: every 60 minutes

get_timeline.py
# -*- coding: utf-8 -*-
import sys, os, re, urllib, urllib2
import simplejson
import tweepy
from datetime import *
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db


#OAuth
CONSUMER_KEY        = 'CONSUMER_KEY'        #自分で取得した値を入れる
CONSUMER_SECRET     = 'CONSUMER_SECRET'     #自分で取得した値を入れる
ACCESS_TOKEN        = 'ACCESS_TOKEN'        #自分で取得した値を入れる
ACCESS_TOKEN_SECRET = 'ACCESS_TOKEN_SECRET' #自分で取得した値を入れる


#データモデルの定義
class TimelineData(db.Model):
    icon_url   = db.StringProperty()                   #アイコン画像URL
    scr_name   = db.StringProperty()                   #スクリーン名
    usr_name   = db.StringProperty()                   #ユーザ名
    tweet      = db.StringProperty(multiline=True)     #ツイート
    status_id  = db.StringProperty()                   #ステータスID
    reply_id   = db.StringProperty()                   #リプライID
    created_at = db.StringProperty()                   #ツイートされた日時
    ds_created = db.DateTimeProperty()                 #DataStoreに登録された日時
    sent       = db.IntegerProperty()                  #送信済みフラグ
    schema_ver = db.IntegerProperty()                  #スキーマ・バージョン



# メイン処理
class GetTimeline(webapp.RequestHandler):
  def get(self):
    #HomeTimelineの取得    
    auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
    api = tweepy.API(auth)
    home_timeline = api.home_timeline(count=50)
    
    #TimeLineの並び順を逆順に変更
    home_timeline.reverse()
    
    for tw in home_timeline:
      try:
        #同じステータスIDが登録されているかCheck
        registed_check_query = TimelineData().all().order('-ds_created').filter('status_id =', str(tw.id).encode("UTF-8")).get()
        
        print registed_check_query
        
        #登録がなければ、登録
        if None == registed_check_query:
          #データストアに登録
          td = TimelineData()
          td.icon_url   = tw.user.profile_image_url                         #アイコン画像URL
          td.scr_name   = tw.user.screen_name                               #スクリーン名
          td.usr_name   = tw.user.name                                      #ユーザ名
          td.tweet      = tw.text                                           #ツイート
          td.status_id  = str(tw.id).encode("UTF-8")                        #ステータスID
          td.reply_id   = str(tw.in_reply_to_status_id).encode("UTF-8")     #リプライID
          td.created_at = str(tw.created_at).encode("UTF-8")                #ツイートされた日時
          td.ds_created = datetime.utcnow() + timedelta(hours=9)            #DataStoreに登録された日時
          td.sent       = 0                                                 #送信済みフラグ、0は未送信
          td.schema_ver = 2                                                 #スキーマ・バージョン
          td.put()
          
          print "▼Check Status"
          
          print td.icon_url.encode("UTF-8")
          print td.scr_name.encode("UTF-8")
          print td.usr_name.encode("UTF-8")
          print td.tweet.encode("UTF-8")
          print td.status_id.encode("UTF-8")
          print td.reply_id.encode("UTF-8")
          print td.created_at.encode("UTF-8")
          print str(td.ds_created).encode("UTF-8")
          print str(td.sent).encode("UTF-8")
          print str(td.schema_ver).encode("UTF-8")
          
          print "△Save DataStore\n"
          
        else:
          print "△Skip DataStore\n"
          
      except:
        print "△NG DataHandling\n"



application = webapp.WSGIApplication(
                                [
                                  ('/main', GetTimeline),
                                ],
                                debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
    main()

send_tweet.py
# -*- coding: utf-8 -*-
import sys, os, re, urllib, urllib2
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
import base64
from google.appengine.api import mail
from dateutil import parser
from dateutil import relativedelta


#データモデルの定義
class TimelineData(db.Model):
    icon_url   = db.StringProperty()                   #アイコン画像URL
    scr_name   = db.StringProperty()                   #スクリーン名
    usr_name   = db.StringProperty()                   #ユーザ名
    tweet      = db.StringProperty(multiline=True)     #ツイート
    status_id  = db.StringProperty()                   #ステータスID
    reply_id   = db.StringProperty()                   #リプライID
    created_at = db.StringProperty()                   #ツイートされた日時
    ds_created = db.DateTimeProperty()                 #DataStoreに登録された日時
    sent       = db.IntegerProperty()                  #送信済みフラグ
    schema_ver = db.IntegerProperty()                  #スキーマ・バージョン


#メイン処理
class SendTweet(webapp.RequestHandler):
  def get(self):
    
    not_send = TimelineData().all().order('ds_created').filter('sent =', 0).fetch(limit=200)
    #print not_send
    
    count = 0   #ツイート数
    start = ""  #最初のツイート
    body = ""   #ツイートの要約
    #"http://twitter.com/#!/"
    
    if not_send:
      for ns in not_send:
        #時刻を変換
        d = parser.parse(ns.created_at)
        jst = d + relativedelta.relativedelta(hours=+9)
        #print jst
        
        try:
          
          body += '
' + "\n" body += '
' + "\n" body += '
' + ns.scr_name.encode("UTF-8") + ' (' + ns.usr_name.encode("UTF-8") + ')' + '
' + "\n" body += '
' + str(jst).encode("UTF-8") + '
' + "\n" body += '' body += '
' + ns.tweet.encode("UTF-8") + '
' + "\n" body += 'http://mobile.twitter.com/' + ns.scr_name.encode("UTF-8") + '/status/' + ns.status_id.encode("UTF-8") + '' + "\n" if 'None' != ns.reply_id: body += '' body += '
' + "\n" body += '
' + "\n" body += "\n" count += 1 if count == 1 : #start = ns.created_at.encode("UTF-8") start = str(jst).encode("UTF-8") ns.sent = 1 #送信済みフラグの変更 ns.put() print str(ns.sent).encode("UTF-8") except: print count print ns.sent #print body subject = start + " から " + str(count).encode("UTF-8") + "件"; print subject fromAddr = "from: メールアドレス"; toAddr = "to: メールアドレス"; html_h = "\n\n" html_f = "" + "\n" html_body = "" html_body += html_h html_body += body html_body += html_f #print html_body mail.send_mail(fromAddr, toAddr, subject, body=" ", html=html_body) else: print "No Data" application = webapp.WSGIApplication( [ ('/send', SendTweet), ], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()

db_delete.py
# -*- coding: utf-8 -*-
import sys, os, re, urllib, urllib2
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db


#データモデルの定義
class TimelineData(db.Model):
    icon_url   = db.StringProperty()                   #アイコン画像URL
    scr_name   = db.StringProperty()                   #スクリーン名
    usr_name   = db.StringProperty()                   #ユーザ名
    tweet      = db.StringProperty(multiline=True)     #ツイート
    status_id  = db.StringProperty()                   #ステータスID
    reply_id   = db.StringProperty()                   #リプライID
    created_at = db.StringProperty()                   #ツイートされた日時
    ds_created = db.DateTimeProperty()                 #DataStoreに登録された日時
    sent       = db.IntegerProperty()                  #送信済みフラグ
    schema_ver = db.IntegerProperty()                  #スキーマ・バージョン



#メイン処理
class DeleteDS(webapp.RequestHandler):
  def get(self):
    # DataStoreの削除
    deleteCheck = TimelineData.all().filter('sent =', 1).fetch(limit=500)
    
    if deleteCheck :
      for dc in deleteCheck:
        try:
          db.delete(dc)
          print "deleted"
        except:
          print "no delete"
    else:
      print "no delete data"


application = webapp.WSGIApplication(
                                [
                                  ('/db/delete', DeleteDS),
                                ],
                                debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
    main()

default.py
print "Content-Type: text/plain"
print ""
print "TwitMail"

まだまだ見直す必要があるコードですが、参考になれば。

2010/12/31

2010年の振り返り

奇跡的にこのブログが1年以上継続できたので、2010年の振り返りをしてみることにします。

まず個人活動としては、

1月
Twitter bot「@kokucheese_bot」作成。AppEngineをPythonに乗り換える。それなりの多くの人の役に立っているようで嬉しい限り。

3月
JavaScript強化宣言。今後の主要言語として外せません。継続してフォローしていきたいと思っています。

5月
Androidアプリ「ぐぐっと!急上昇ワード」公開。その後も継続してアップデートし、現在約27,000ダウンロード。こちらも多くの人の役に立っているようで嬉しい限り。

7月
スマートフォン向けJavaScriptライブラリ「iUI」を試す。

9月
注目度がとても高いサーバーサイドJSの「Node.js」をさわってみた。

10月
スマートフォン向けJavaScriptライブラリ「JQuery Mobile」を試す。

11月
AIR for Androidで作成したアプリ「Simple Timer」を公開。
アプリ紹介ページ作成。これもAppEngine上に公開。静的ページだけでもAppEngine。

12月
Twitterのタイムラインをタイムシフト視聴するためのWebアプリケーション作成。これもAppEngineを活用。

キーワードを抽出すると、
  • Google App Engine
  • JavaScript
  • Twitter
  • スマートフォン
  • AIR/AS3
ってところでしょうか。

上記以外にも、RailsやSinatraでrubyに触れたり、NoSQLであるMongoDBを触ってみたり、スマートフォン向けサイトでhtml5&CSS3を使い始めてみたりしてます。あと、Titanium MobileでiPhone向けアプリにも挑戦中です。

仕事では、当時在職していた会社で、メイン事業の携帯アプリのデザインと新規開発の携帯アプリのデザインを手掛けたりしました。
また、たくさんの勉強会やセミナーに参加できて、仕事では関われないようなクラウド系、大規模系、インフラ系等の話が聞けたりしたのもかなりの収穫でした。

それと書籍のレビューをお手伝いし、謝辞を戴いたことも初めての経験でした。

今年1年、たくさんの方々のおかげで、小さいながらも公開できるモノを作ってこれました。この場をかりて厚く御礼申し上げます。
私も誰かの役に立てるようにできるだけ情報発信に努めたいと思っております。
そして近いうちに新しいアプリの公開やWebサービスの実現へ繋げていきたいと考えています。

今年も成長曲線を描けたと思います。(一応このブログのタイトルなので…)
そして来年も(成長曲線を描けるように)頑張りまっせ〜

2010/12/11

TwitterとGoogle App Engineの自分用Webアプリケーション

最近タイムラインがリアルタイムで追えなくなってきたり、過去にみたアレのURLが知りたいとかいったときのために、自分のHomeタイムライン(自分とフォローしている人のツイート全部)を保存して、後で閲覧できるようにしたいなぁと思っていた。
とりあえず、タイムラインのデータを取得して、保存するところまでをGoogleAppEngineで作ってcronを回していたら、2ヶ月ぐらいでDataStoreの無料枠1GB一杯になって、止まってしまっていた。

一定期間が過ぎたツイートは順次削除していくことも考えたが、せっかく貯めたデータを消してしまうのはもったいないし、こういった機能が無いから作るわけで、何か良い解決策は無いものかと思っていた。(課金してまで作ろうという発想はなかった)それと、保存したデータをどうやって閲覧するかも問題で、本気でつくると結構な作業になる。

そんなこんなで、しばらく経ったある日、よく他人のツイートを紹介するのに使われる、1ツイートだけの画面を見た時に、ここにもリツイートやら返信やらの機能があって、ツイート1つ1つに対して付与されているステータスIDとユーザ名でURLを特定できることに思い至った時に、コレでかつる!とひらめいた。

つまり、取得したタイムラインのデータを整形して、自分宛にメール送信する。ツイート文と共に上記のURLを記載しておけば、メーラーでタイムラインを追いつつ、必要があればURLからWebに飛んで、リツイート/返信/お気に入り等の機能も使える。なおかつメール形式にすることで、アーカイブと検索機能も手に入る。といったお手軽かつ要件も充分に満たしたWebアプリケーションを思いついた。(ツイートはDataStoreに登録しておき、一定時間毎にをまとめてメール。送信済みフラグの立ったのデータは順次DataStoreから削除)

現在1週間ほど稼働してますが、問題なく動いている。

そしていくつか課題も見えてきた。
1.メール送信にTaskQueueを使う
2.時刻変換の調査
3.メールをもっと見やすく
4.メンテナンス時の対応

1.メール送信にTaskQueueを使う
ここはTaskQueueを使うべきと思ってはいますが、TaskQueueをまだちゃんと理解してないので未実装。追って対応予定

2.時刻変換の調査
文字列から時刻に変換して、日本の時間に変換する。→なぜかエラーになるので調査中
(現状ツイートされた時間を日本時間に変換できていない)
※12/12 dateutilを使うことで対応。これ便利ですね。

3.メールをもっと見やすく
どんなに頑張ってもテキストだと限界がある。HTMLメールにして、読みやすくしたい
※12/12 HTMLメール対応。しかしツイート本文のリンクが…

4.メンテナンス時の対応
自分宛にメッセージ出してもしょうがないので、何の対応もしていないが、どうしますかねぇ

ぼちぼちやっていきますか。

2010/01/03

Google App Engineで動かすTwitter Botをつくってみた

先日(もう去年ですが)「BOTつくろう会#5」の勉強会に参加させていただいた。@tetsunosukeさんの初心者教室でPythonでのBot作成について教えていただいたので、そのままPythonでBot作りに挑戦してみた。

最初にどうゆうBotを作ろうかと考えた時、いつも勉強会の参加時にお世話になっているATNDの新着イベントをつぶやく@atnderが頭に浮かんだ。ATND以外に「こくちーず」というサイトでもちょっとだけIT系の勉強会の告知/募集が行われている。では、@atnderの「こくちーず」版を作ってみたらどうだろうか。というわけで、Botの大枠は決まった。

次に、Botの要件をつめていこう。ATNDはAPIが公開されていて、開催イベントについて色々な情報の取得が容易になっている。一方こくちーずの方は、新着情報のRSSがある程度。まずは「新着イベントをRSSから取得し、イベント名と告知ページのURLをTweetするBot」とし、「URLはBit.lyの短縮URLに変換する」とした。

しばらくローカル環境でごにょごにょ試した結果。以下の環境に落ち着いた。
※開発はMacでやっている。PythonはMacPortsで2.6.xも入れていたが、Mac OS Xに標準で入っているVer.2.5.1(10.5の場合)でやることにした。

あと、Feedの取得/パースには結構時間がかかるようで、全ての処理をひとまとめにすると30秒制限に引っ掛かることがあったため、「Feedの取得からDataStoreの登録」「DataStoreから情報を取得しTweet」という2つの処理に分け、それぞれにCronを設定する構成にした。

ファイル構成はこんな感じ
kokucheese_bot
├app.yaml      …自動生成されたファイルを編集
├cron.yaml     …自動生成されたファイルを編集
├index.yaml    …自動生成されたファイルを編集
├default.py    …新規作成
├get_feed.py   …新規作成
├tweet.py      …新規作成
├feedparser.py …DLしたファイル
├simplejson/   …DLしたフォルダ
└twython/      …DLしたフォルダ

app.yaml
application: アプリケーション名
version: 1
runtime: python
api_version: 1

handlers:
- url: /
  script: default.py
  
- url: /get_feed
  script: get_feed.py
  
- url: /tweet
  script: tweet.py
  
- url: /.*
  script: default.py
※cronで叩けるようにプログラムとURLを関連付ける。

cron.yaml
cron:
- description: get_feed job
  url: /get_feed
  schedule: every 30 minutes

- description: tweet job
  url: /tweet
  schedule: every 30 minutes
Cronの間隔は更新頻度と負荷を考えて、とりあえず30分間隔に。これで最大1時間以内にはTweetされることになるはず。
※頻繁にFeedを取得しにいくのも悪いかなと思ってしまうのですが、Googleリーダーとかってどのくらいの間隔で取得しにいくものですかねぇ。

default.py
print "Content-Type: text/plain"
print ""
print "kokucheese_bot"
※AppEngineのURLに / でアクセスしたり、存在しないURLでアクセスしたりすると表示される。
※このBotにはWebページ的なものが必要ないので、表示はすべてprint文で手抜きした。

get_feed.py
# -*- coding: utf-8 -*-
import sys, os, re, urllib, urllib2
from datetime import *
import feedparser
import simplejson
from google.appengine.ext import db


#データモデルの定義
class EntryDataModel(db.Model):
    increment = db.IntegerProperty()   #登録数(連番)
    title     = db.StringProperty()    #イベントタイトル
    link      = db.StringProperty()    #告知ページのURL
    date      = db.StringProperty()    #Feedに登録された日時
    short_url = db.StringProperty()    #短縮済みURL
    created   = db.DateTimeProperty()  #DataStoreに登録された日時
    tweeted   = db.IntegerProperty()   #Tweet済みフラグ


#現在の登録数を返す
def get_inc_num():
    if 0 != EntryDataModel.all().count(limit=1):
        inc_num = EntryDataModel.all().order('-increment').get()
        return inc_num.increment
    else:
        return 0


#短縮URL化(bit.ly API)
def get_short_url(url):
    info_url  = "http://api.bit.ly/%s?version=2.0.1&%s=%s&login=bit.lyログイン名&apiKey=APIキー"
    url_data  = urllib2.urlopen(info_url % ("shorten", "longUrl", urllib2.quote(url))).read() #urllib2.quote(url)…パラメータ付URLもあるのでURLエンコードする
    url_info  = simplejson.loads(url_data)
    return url_info["results"][url]["shortUrl"]


#結果を表示
def check():
    request_all_query = EntryDataModel.all()
    result_set = request_all_query.order('-created').fetch(limit=50) #最新の50件
    for rs in result_set:
        print "No. "     + str(rs.increment).encode("UTF-8")
        print "Title: "  + rs.title.encode("UTF-8")
        print "Link: "   + rs.link.encode("UTF-8")
        print "Update: " + rs.date.encode("UTF-8")
        print "Short: "  + rs.short_url.encode("UTF-8")
        print "Create: " + str(rs.created).encode("UTF-8")
        print "Tweet: "  + str(rs.tweeted).encode("UTF-8")
        print ""


#メイン処理
def main():
    print "Content-Type: text/plain"
    print ""
    
    #情報入手元Feed
    feed_urls = [
        "http://kokucheese.com/main/rss/",
        #"",
        #"",
        ]
    
    #Feedから各エントリを取得し処理
    for feed_url in feed_urls:
        feed_data = feedparser.parse(feed_url) #Feedをパース
        
        #エントリの順番を並び替える
        reverse_ent = []
        reverse_ent = feed_data.entries
        reverse_ent.reverse()
        
        #現在の登録数を取得
        num = get_inc_num()
        
        for ent in reverse_ent:
            try:
                #同じタイトル及びURLが登録されているかCheck
                registed_check_query = EntryDataModel.all().filter('title =', ent.title).filter('link =', ent.link).count(limit=100)
                
                #登録がなければ、登録
                if 0 == registed_check_query:
                    t = ent.title
                    u = get_short_url(ent.link)
                    
                    num += 1
                    
                    #データストアに登録
                    feed_source = EntryDataModel()
                    feed_source.increment = num
                    feed_source.title     = t
                    feed_source.link      = ent.link
                    feed_source.date      = ent.updated
                    feed_source.short_url = u
                    feed_source.created   = datetime.utcnow() + timedelta(hours=9)
                    feed_source.tweeted   = 0
                    feed_source.put()
                    
            except:
                print "skip: " + ent.title.encode("UTF-8")
                
    check()


if __name__ == "__main__":
    main()
※Feedの取得からDataStoreへの登録
※参考にしたサイト:bit.lyのAPIを試してURLを短縮してみた
※複数のFeedを登録可能。でも増やし過ぎると30秒で完了しなくなる。そうなってくると Task Queue 使うとかロジックの見直しが必要。
※Feedのパースは Universal feed parser を使えば楽チン。GAE/Jだと ROME が動かないっぽいので、自前パーサを作るとかしないといけないかも。でもJavaでXMLを扱うならGroovyで書く方が良いと思う。
※トランザクションの使用については今後の課題。

tweet.py
# -*- coding: utf-8 -*-
import sys, os, re, urllib, urllib2
import simplejson
import twython
from google.appengine.ext import db


#データモデルの定義
class EntryDataModel(db.Model):
    increment = db.IntegerProperty()   #登録数(連番)
    title     = db.StringProperty()    #イベントタイトル
    link      = db.StringProperty()    #告知ページのURL
    date      = db.StringProperty()    #Feedに登録された日時
    short_url = db.StringProperty()    #短縮済みURL
    created   = db.DateTimeProperty()  #DataStoreに登録された日時
    tweeted   = db.IntegerProperty()   #Tweet済みフラグ


#結果を表示
def check():
    request_all_query = EntryDataModel.all()
    result_set = request_all_query.order('-created').fetch(limit=50) #最新の50件
    for rs in result_set:
        print ""
        print "Title: " + rs.title.encode("UTF-8")
        print "Tweet: " + str(rs.tweeted).encode("UTF-8")


#メイン処理
def main():
    print "Content-Type: text/plain"
    print ""
    
    #未Tweetのエントリを検索
    tweeted_check_query = EntryDataModel.all()
    untweet_set = tweeted_check_query.filter('tweeted =', 0)
    for tw in untweet_set:
        #Tweetする
        message = tw.title + u" が登録されたよ " + tw.short_url #twythonにはunicode文字列を渡す。日本語で140文字OK
        twitter = twython.core.setup(username="Twitterユーザー名", password="Twitterパスワード") #Twython v1.0
        twitter.updateStatus(message)
        
        #Tweet済みフラグを更新
        tw.tweeted = 1
        tw.put()
    
    check()


if __name__ == "__main__":
    main()
※DataStroreから未Tweet分をTweet
※Twython Ver.0.8では日本語140文字分をTweetするにはソースの修正が必要だが、Ver.1.0では必要なし。

以上のファイルをAppEngineにアップロードすればBotとして動くはずです。

RSSの他にもGoogleアラートや以前の投稿Twitterのつぶやきをキーワード検索させる等、Feedとして得られる情報はたくさんあるので、今回のBot以外にも使い方はいろいろありそうです。

今後はatnderみたいにリマインダー機能とか追加していきたいですね。

完成したBotはこちらkokucheese_bot

※そもそも勘違いしている箇所や、よりより書き方があったりする知れませんがご容赦ください。またその場合はご指摘いただければ幸いです。

※現在はTwitter側の仕様変更によりBasic認証が使えなくなりました。上記のコードそのままでは投稿できません。

※GAE+Twitter+OAuth関連のソースは『TwitterとGoogle App Engineの自分用Webアプリケーション「TwitMail」(ソース)』にも上げてあります

2009/10/11

Twitterのつぶやきをキーワード検索させる

何かTwitterを使ったプログラムを作ってみようかと、Twitter API 仕様書 日本語訳を眺めながらアイデアを考えていた。
自分がフォローしていないユーザの発言から気になるキーワードで検索して一覧を返してくれたら、いちいち検索する必要がなくなるじゃん。ってなカンジでsearch系APIを探すと…

URL: http://search.twitter.com/search.format
 (format は json, atom のうちのいずれかを指定)


ほうほう、JSONかATOMを返してくれるのかぁ。あ!
RSSリーダにAPI形式のURLごと登録してしまえば、それでいいじゃん!

あえなく、最初のアイデアは没になった。。。

以下やり方。

http://search.twitter.com/search.atom?lang=ja&q=scala
上記URLで“scala”という発言の含まれた日本語の発言を対象に検索してくれます。

複数の検索キーワードを設定したい場合は、カンマで区切ります。
http://search.twitter.com/search.atom?lang=ja&q=scala,lift

検索キーワードに日本語を指定したい場合は、URLエンコードしましょう。
例えば、“セカイカメラ”を検索キーワードに設定したい場合、一度検索キーワードが日本語の状態のURLをブラウザに入力します。
http://search.twitter.com/search.atom?lang=ja&q=セカイカメラ



ページの情報を表示(Firefoxの場合)でURLエンコードされたURLが手に入ります。



このURLをRSSリーダ等に登録すれば、あとは自動的に検索結果を入手できるようになります。
私はiGoogleに登録しました。