概要
「ツイートのIDがわかっていてツイートのテキストを取得したい」という状況があり、
Twitter API
では制限が 180GET/15minutes
で遅かったので、
URLからHTMLをとってきてパースする単純なGo
コードを書いたのですが、
リポジトリを作ってパッケージ化してしまおうかなと思った次第です。
パッケージの作成を通して自分が使っている便利ツールを紹介したいと思います。
また、コメントで他の開発便利ツール教えていただけたら嬉しいです。
Go言語について
Go
は開発支援ツールが充実している気がします。気がします。
あとは、ここでは触れませんがchannel
を使うと並行処理が割と簡単にできます。
(例えば、500個のツイートを取得するために、100個のループ5つを並列に回したり。)
あと、入門系記事にあんまり書いてありませんが、便利なのが構造体のタグです。
(jsonのキー名決めたり、ORMのカラムの型決めたりする時。reflect
で取れる。)
パッケージ作成
大まかな仕様
- APIでデータを取得する方法は考慮しない
- 取得したいツイートのURLは既知
- エラーを拡張して何によるエラーなのか分類
- HTML内に含まれる(一連の会話)リプライも取得
手順
- リポジトリ作成
- ファイル作成
- テストコード
- ソースコード
- ドキュメント
リポジトリ作成
リポジトリを作ります。
GoDoc、Go Search、GitHubで検索してみます。
simpletwitterなるものをGitHub
で作りました。(.gitignore
とかはGitHub
にGo
指定して自動生成してもらいました。)
GitHub
とかじゃないプライベートリポジトリの場合、URL的に色々障壁があるみたいです。
最初はまだ何もソースがないので -d
オプションつけて get
してください。
go get -d github.com/sosuke-k/simpletwitter
ファイル作成
ファイル名はパッケージフォルダ直下にあれば何でもいいですがとりあえず tweet.go
を作成しました。パッケーシ名はsimpletwitter
にしました。
【便利ツールその1】goimports
goimports - The Go Programming Language
Go
の書式を修正してくれるツールにgofmt
がありますが、goimports
は更に使われているパッケージのimport
まで自動で付加してソートしてくれます。(使われてないものが消されてしまいます。)
命名規則にあってないものや、パッケージ外から使える関数(大文字から始まる)などを定義した時に説明コメントがないものなど、色々警告してくれます。
go get code.google.com/p/go.tools/cmd/goimports
で使えます。
※ atomユーザはgo-plusを入れることで保存時に実行してくれます。
テストコード
testing - The Go Programming Language
create a file whose name ends _test.go that contains the TestXxx functions as described here
ので、tweet_test.go
を作りました。
assert
こちらで言われていますが、
Go には標準 assert がない
思想なのでしょうが、、、自分は特にこだわりません。自分の置かれている環境で便利な方がいいです汗
【便利ツールその2】goconvey
GoConvey - Go testing in the browser
it's more expressive than t.Errorf()
はい そっちがいいです。イントロビデオがあるので、最初の方だけでも見ると雰囲気わかります。
go get github.com/smartystreets/goconvey
cd $GOPATH/github.com/sosuke-k/simpletwitter
goconvey
goconvey
コマンドを実行するとブラウザが開きます。
テーマを変更したり、デバグ出力の切り替えや監視フォルダを指定したりできます。
最初はgoconvey
を実行したディレクトリが監視フォルダになっています。
上書き保存すると自動でテストを実行し結果をブラウザ上に表示してくれるので、シンプルなエディターを使っていてマルチディスプレイとかだとめちゃくちゃ捗りますし、かっこいいです。
Composer
func NewTweet(url string) (tweet Tweet, err error) {
return
}
上のような関数のテストを書きたい時に、下図左のようなテキストを入力すると下図右のような雛形を出力してくれます。
Composer
は正直、あまり使わないです。。。
Conveyテストコード
基本的にConvey
関数の3つ目の引数に無名関数でテスト書きます。
先ほどの雛形をtweet_test.go
に貼り付けると以下のような出力に変わります。
以下のようなテストにすると
func TestNewTweet(t *testing.T) {
Convey("Given available first post tweet url\n", t, func() {
url := "https://twitter.com/olha_drm/status/418033807850496002"
tweet, err := simpletwitter.NewTweet(url)
So(err, ShouldEqual, nil)
So(tweet.Text, ShouldEqual, "よるほ あけましておめでとうございますほー")
So(tweet.ScreenName, ShouldEqual, "olha_drm")
So(tweet.Name, ShouldEqual, "織羽")
So(len(tweet.After), ShouldEqual, 1)
So(tweet.After[0].Text, ShouldEqual, "@olha_drm あけおめっ!ことよろー!!")
So(tweet.After[0].ScreenName, ShouldEqual, "reprohonmono")
})
}
こんな感じ。
【便利ツールその3】go-spew
デバグとかで使えるpretty print系です。
spew.Dump(tweet)
をテストコードに追加すると
こんな感じです。
ソースコード
補完系だとgocodeがあります、、が、あまり良さがよくわかっていないので特に推しません。
※ atomユーザはautocomplete-goを入れることで使えます。
【interfaceとは】エラーの拡張
エラーの拡張を通して interface
をなんとなく知ります。
まずerror
の定義です。
type error interface {
Error() string
}
つまり、文字列を返すError()
とう関数を持っていればいいわけです。
ここでは
- どんなオペレーションを行っていた時のエラーなのか
- どんなURLにアクセスしようとしたのか
- どんなURLにリダイレクトされたのか
といった情報を持ち、文字列を返すError()
とう関数を持っているError
という構造体を定義します。
type Error struct {
Op string // the failing Operation (Request, Redirected, Parse)
GetURL string // the getting url
RedirectedURL string // the definitive url
Err error // the reason the get failed
}
func (e *Error) Error() string {
return "please input error log"
}
ソースコード内では
doc, err = goquery.NewDocument(url)
if err != nil {
err = &Error{
Op: "Request",
GetURL: url,
Err: err,
}
return
}
if url != doc.Url.String() {
err = &Error{
Op: "Redirected",
GetURL: url,
RedirectedURL: doc.Url.String(),
}
return
}
のように拡張したエラーを返します。
パッケージを使用する側では
Convey("Given non-available tweet url\n", t, func() {
url := "https://twitter.com/statuses/418033823511629836"
_, err := simpletwitter.NewTweet(url)
So(err, ShouldNotEqual, nil)
So(err.(*simpletwitter.Error).Op, ShouldEqual, "Redirected")
})
のように型アサーションして使います。
また、そのまま、他パッケージの関数のerr
をそのまま使いたいのであれば
func (e *Error) Error() string {
return e.Err.Error()
}
とすればいいわけです。
このように、後でログ分析しやすいように Error()
関数の出力を調整することができます。
なんとなくinterface
がわかった気がしないでもないです。。。
ドキュメント
【便利ツールその4】godoc
go get golang.org/x/tools/cmd/godoc
で入ります。
godoc -http=:6060
して、http://localhost:6060 にアクセスしてみます。
$GOPATH
下にあるパッケージが見れます。
今回の場合は http://localhost:6060/pkg/github.com/sosuke-k/simpletwitter/ を見てます。
まだ何も書いてないのにある程度中身がわかります。
package xxx
の上や関数や構造体の宣言の上にコメントを書くことで godoc
で見た時に反映されるので、書いていきます。
例えば、simpletwitter.go
を作り
/*
Package simpletwitter uses goquery(https://github.com/PuerkitoBio/goquery) to fetch tweets.
Example:
tweet err := simpletwitter.NewTweet(url)
if err != nil {
if err.(*simpletwitter.Error).Op = "Redirected" {
...
}
}
fmt.Println(tweet.ScreenName + " : " + tweet.Text)
*/
package simpletwitter
とすると
のようなOverview
ドキュメントが作られます。
コメント内でインデントすることで、その行がコードシンタックスになるみたいです。
終わりに
長くなりました。
ドキュメントがしっかりしてないライブラリは使う気にならないですよね。
かと言って、作った側は十分使えるわけで、めんどくさいですよね。
オープンソース系にコミットしている人たちはほんとに素晴らしいと思います。