2012年05月24日

golangでTwitterのタイムラインをブラウザに表示させてみた。

まえおき

Andrew Gerrandさんのgithubはgolangの宝の宝庫。その中に https://github.com/nf/gotweetというTwitter APIを使うサンプルがあったので、 参考にさせてもらって、それにGAEのチュートリアルをあわせて、 Twitterのuser_timelineをブラウザに表示させるようにしてみました。

準備

gotweetのプロジェクトをcloneします。(あとで名前をgotweet_changeプロジェクトに変更しています)

% cd workspace/
% git clone github.com/nf/gotweet

READMEにも書かれてますが、mrjonesさんのoauthライブラリを使用するので、まずインストールしてください。 go install とありますが、うまく動かなかったので、clone しています。 (goファイルのimport文には"github.com/mrjones/oauth"となっているのを "oauth"と変更しています。)

% git clone github.com/mrjones/oauth

clone先のディレクトリは、僕が確認したのは、myappのGAEのプロジェクトだとすると、myappディレクトリの直下においてください。 下図のようなイメージです。

gotweet_change/
    app.yaml
    hello/
        hello.go
    oauth/
    template/
        tweet.html

READMEには、ドキュメントを読んでください、と書いています。oauthライブラリの使い方の説明などが書いています。 (まだ軽くしか読んでないので、いずれちゃんと読みます。。)

godoc github.com/mrjones/oauth | less

実際にTwitterAPIを使用する際は、 Consumer IDとConsumer Secret をTwitter Devloperサイトから取得しておいてください。

ソースごにょごにょ

全体のソースはgithubにあげました。 忘れそうなポイントだけ書いておくことにします。

JSONデータのたくさんのパラメータから、type structで取得したいパラメータ名のみを定義すると、 そのパラメータのみ取得できます。(取得したいデータを絞り込むことができます)

// 取得したいパラメータをstructで記述
// 参考 https://dev.twitter.com/docs/api/1/get/statuses/mentions
type TweetObject struct{
    Created_at string
    Id_str string
    Text string
    Source string
    In_reply_to_user_id_str string
    User UserObject // JSONオブジェクト内のオブジェクトをこのように定義する。
}

// JSONオブジェク内のオブジェクト
type UserObject struct{
    Id_str string
    Name string
    Screen_name string
}

下記は、TwitterのtimelineをGETしてパースしてテンプレートに出力しているところです。
コメント参照。

resp, err := consumer.Get(url,nil, &atoken) //TimelineをGetしてrespに格納

w.Header().Add("Content-type","text/html charset=utf-8") // ヘッダー追加
body, err := ioutil.ReadAll(resp.Body) //respを読み込んでbodyに格納

var tweets []TweetObject // 実体を宣言
err2 := json.Unmarshal(body,&tweets) // UnmarshalはJSONエンコードデータをパースします

t,err:= template.ParseFiles("template/tweet.html")  // gotweet_change/template/tweet.html に配置
t.Execute(w, tweets) // tweet.htmlにtweetsを埋め込んで表示する

Go version 1 になってからかなりのパッケージが変更になっています。詳細は以下のページが分りやすいかもです。
http://golang.org/doc/go1.html#packages

たとえば、"json""json"だけでよかったのが、"encoding/json"と変更になっています。

7行目のjson.Unmarshalについて、 JSONエンコードされたデータ(ここではbody)を解析し、tweetsに格納しています。

template.ParseFiles()の引数で テンプレートとなるファイルを指定することが出来ます。template.ParseFiles("template/tweet.html")と書くと、 gotweet_changeプロジェクトであれば、gotweet_change/template/tweet.htmlとしてテンプレートファイルを作成します。

また、template.ParseFiles()の引数には、複数ファイルを指定することができ、 たとえば、header.html、main.html、footer.htmlなどに分けることができます。

さて、tweet.htmlの中身は以下のようになっています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <style type="text/css">
        div.tl  {width:600px;}
        table,td {border: solid; border-collapse: collapse;}
    </style>
</head>
<body>
<div class="tl">
    <table border cellspacing=0>
    {{range .}}
        <tr>
        <td>
        日時:{{.Created_at}}<br/>
        ツイート:{{.Text}} <br/>
        スクリーン名:{{.User.Screen_name}}
        </td>
        </tr>
    {{end}}
    </table>
</div>
</body>
</html>

{{range .}} T1 {{end}}T1を繰り返します。ドットは、この例ではtweetsです。 {{.Created_at}}{{.Text}}は、TweetObjectのメンバ変数に当たるもので、それらを表示します。

{{.User.Screen_name}}UserObjectのメンバ変数Screen_nameを表示するということです。 これらの書き方は、http://golang.org/pkg/text/template/#Actionsが参考になると思います。

これを実行すると、下図のように、ユーザータイムラインを取得することができました。

実行結果

参考

jsonのUnmarshalについて参考にしたgist
https://gist.github.com/775526
Templateについて参考にしたgolang_guestbook
https://bitbucket.org/IanLewis/golang_guestbook/src/ece99a50ddfa/templates/base.html
タグ:golang Go
posted by やし at 00:17 | Comment(4) | golang | このブログの読者になる | 更新情報をチェックする

     このエントリーをはてなブックマークに追加 このエントリーを含むはてなブックマーク
この記事へのコメント
スマイム | URL
はじめまして スマイムと申します。

GAE/golang上で本記事のようなコード(json読み書きなどカットし簡素化)を動かしたのですが
oauth.Get()の戻り値errは !=nilで、そのerr.Error()は、
Do: Get http://api.twitter.com/1/statuses/user_timeline.json: permission denied
をはいていました。スタンドアロンで動くようにしたコード(%go build twittergo.go;./twittergo)では
twitterのタイムラインを表示できています。。

どうしてでしょう。。

※ access.json,-id=,-secret=はスタンドアロンでは表示されているので正しく設定していると思います。。




2012年09月16日 12:04

やし(管理人) | URL
スマイムさん、はじめまして。
コメントありがとうございます。

たしかにこのブログの記事でデプロイまでするとpermission deniedはきますね。
すいません、ローカルまでしか確認してかなかったです。

私も調べてみたのですが、おそらく下の記事に書かれているようなことが原因と思われます。
http://blog.to9da.com/2012/07/go-on-app-engine-using-google-api.html

"appengine/urlfetch"を使えばできるようですが、具体的にどうしていいかが分かりませんでした。
もしスマイムさんがお分かりになるようでしたら、逆に教えて頂きたいなぁなんて(^^ゞ

具体的な解決方法を提示できなくて申し訳ありませんが、よろしくお願いします。
2012年09月17日 12:01

スマイム | URL
やしさん、ありがとうございます。
私の方もちょっと調べてみましたらできました。
oauth.goとoauth.NewConsumer()の呼び出し側を変更したら動くようになりました。

oauth変更箇所
1. oauth.NewConsumer()の引数にappengine.Contextを追加(importに"appengine","appengine/urlfetch"も追加)
2. NewConsumer()内の&Consumer{}内の
 HttpClient: &http.Client{}, を
HttpClient: urlfetch.Client(c), に変更(このcは、↑の追加した引数appengine.Context)

NewConsumer呼び出し側(ハンドラの中から)
func handler(w http.ResponseWriter, r *http.Request){
oauth.NewConsumer(appengine.NewContext(r), ...)

修正はこれだけでした。。
2012年09月17日 13:20

やし(管理人) | URL
おー!スマイムさん、ありがとうございます!
言われた通りやってみたら私もできました!

ただ、ライブラリ側をいじってるので、
ライブラリ側をいじらなくていいようなのってできないですかね。。。

とにかくありがとうございました〜!
2012年09月17日 15:34

コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。