Twitter のバックアップをCouchDBへ。
今年になってからtwitterを使い始めたのですが、気がついたら3200ポストに到達していました。というわけでバックアップ。どうせなら、うちのバキュームカーなんでもDBになりつつある、ESXi 上のCouchDBに放り込んでおく。
はじめてのtwitter APIプログラム。正しいのかどうか不明。というか3200件全部メモリに展開しなくても;;
#!/usr/bin/env ruby require 'rubygems' require 'json' require 'optparse' require 'twitter' mail = nil pass = nil $verbose = false opt = OptionParser.new opt.on("-u [VAL]") { |v| mail = v } opt.on("-p [VAL]") { |v| pass = v } opt.on("-d") { |v| $verbose = true } opt.parse!(ARGV) def log(msg) $stderr.puts "[INFO] #{msg}" if $verbose end # -- main PER_PAGE = 200 unless mail $stderr.print "Type your address for Twitter: " mail = $stdin.gets().chomp end unless pass $stderr.print "Type your password for Twitter: " pass = $stdin.gets().chomp end log "Start to fetch timeline with (#{mail}, #{pass})." log "--" auth = Twitter::HTTPAuth.new(mail, pass, :ssl => true) client = Twitter::Base.new(auth) tl = client.user_timeline(:count => PER_PAGE) first = tl.first unless first $stderr.puts "No timeline exists!" exit 1 end screen_name = first["user"]["screen_name"] log "Screen name is '#{screen_name}'" tls = tl if tls.length == PER_PAGE # fetch with pagination page = 2 while page * PER_PAGE <= 3200 log "Fetch page(#{page})." tl = client.user_timeline(:page => page, :count => PER_PAGE) tls = tls + tl if tl.length < PER_PAGE log "Detected end of timeline (requested #{PER_PAGE} statuses but fetched #{tl.length} statuses." break else page = page + 1 end end end log "All pages have been fetched completely." log "Timeline length = #{tls.length}" log "--" doc = { "docs" => tls } puts doc.to_json
で、とる。
$ ./backup.rb -u XXXX -p PPPP -d > tweets.json
で、あげる。
$ curl -X PUT http://localhost:5984/twitter_backup $ curl -X POST -d @tweets.json http://localhost:5984/twitter_backup/_bulk_docs
これだけじゃなんなので、ViewとList作った。
// views/by_date/map.js function(doc){ emit(new Date(doc.created_at), doc); }
// lists/timeline.js // API _list/timeline/by_date function(head, req){ // !code vendor/couchapp/date.js // !code vendor/ejs/ejs_production.js // !code lib/helper.js // !json templates.timeline.head // !json templates.timeline.row // !json templates.timeline.tail provides("html", function(){ send(template(templates.timeline.head, {})); while(row = getRow()){ send(template(templates.timeline.row, {doc: row.value})); } send(template(templates.timeline.tail, {})); }); }
// lib/helper.js function template(t, b){ return new EJS({text: t}).render(b); } function html_escape(s){ return s.toString().replace(/&/g, "&"). replace(/\"/g, """). replace(/\'/g, "'"). replace(/</g, "<"). replace(/>/g, ">"); } function h(s){ return html_escape(s); } function json_escape(s){ return s.toString().relace(/&/g, "\\u0026"). replace(/</g, "\\u003c"). replace(/>/g, "\\u003e"); } function j(s){ return json_escape(s); }
<!-- templates/timeline/head.html --> <html> <body> <ul> <!-- templates/timeline/row.html --> <li> <span class="created_at"><%= h(doc.created_at) %></span> <span class="screen_name"><%= h(doc.user.screen_name) %></span> <span class="text"> <%= h(doc.text) %> </span> </li> <!-- templates/timeline/tail.html --> </ul> </body> </html>
あ、couchapp 以外にもお気に入りEJS使ってますね。lib/helper.js はローカルでよく使う関数ライブラリで、couchapp generateのときに自動生成するようにCouchApp自体を書き換えています。
http://localhost:5984/twitter_backup/_design/app/_list/timeline/by_date?descending=true でアクセスすると ul/li で3200件全部表示されます。CouchApp で10分作業ですね。
TODO
次は6000件で実行するとして、差分で抽出するようにしないと3200から400件分ぐらい重複しそうなので、その辺を考えよう。6000ポスト当たりになったら。
でも、せっかくのネタなので。
12・1月予定のCouchDB ハッカソン => Relaxon (named by id:z_ohnami) のネタにしようかと思います。(私はもう満足しちゃいましたが。)
Raindrop は?
さすがに現バージョンでバックアップとして機能するわけではないですね・・・。