Twitter のアーカイブを取得するツールをリファクタしてみた
Twitterのアーカイブを取得するツール作りました。No Use API版 - Seasons.NETのスクリプトを
OptionParser や scrapi の勉強がてらにリファクタしてみた。
リファクタといっても僕なりに読みやすいように書き換えただけなので
そんなの全然リファクタリングになってないよ!っていう厳しいツッコミや
ここはこうした方がいいんじゃね?っていうやさしい指摘など、歓迎します。
元のスクリプトとの違い
大まかなものをあげるとこんな感じ。
- スクリプト中にユーザ名やパスを書くのは嫌だったので別途「.twitter-user-pass.rb」を読み込むようにしている
- スクリプトを貼るときに誤って自分のを晒す危険をなくしたかった。それくらいの意味しかないかも
- スクレイピングするページ数の指定を「-p」オプションでする
- 元のスクリプトでは「-s」でする
- 「-l」オプションで結果を保存するファイルを指定できる
- 「-b」オプションで「-k」で指定したキーワードを [] (大括弧)で囲んだものとして扱う
- 標準出力にも結果を出力する
その他の細かい違いは割愛。
使い方
以下のような「.twitter-user-pass」を用意しておいて
USERNAME = 'your_username' PASSWORD = 'your_password'
ruby twitterarchivefilter.rb -k vim -p 10
を実行する。
すると、自分の最近の発言「10」ページ分から、キーワード「vim」が付いているものを拾ってくる。
スクリプト
「UTF-8」で保存してね!
twitterarchivefilter.rb
#!/usr/bin/env ruby -Ku $KCODE = "u" require 'rubygems' require 'scrapi' require 'pp' require 'net/http' require 'kconv' require 'optparse' require '.twitter-user-pass' $stdout.sync = true BASEPATH = '/account/archive' logfilename = 'archive.log' class TwitterArchiveFilter def initialize (keyword, pagenum, use_brace) @items = [] @pagenum = pagenum @http = Net::HTTP.new('twitter.com', 80) @keyword_reg = use_brace ? /\[#{keyword}\]/i : /#{keyword}/i end def filter getArchives() end def dump(filename) open(filename, "w") do |f| @items.each do |msg, time| f.puts "#{time} : #{msg}" puts "#{time} : #{msg}".tosjis end end end private def addItems(items) return unless items @items.concat items[:messages].zip(items[:times]) end def getArchives count = 0 nextlink = BASEPATH while(nextlink) break if count == @pagenum html = getPageArchive(nextlink) addItems getItems(html) nextlink = getNextLink(html) puts "GetPage [#{count += 1}]" end @items = @items.reject{|msg, time| msg !~ @keyword_reg } if @keyword_reg end def getItems(html) items = Scraper.define do process 'td.content>span.entry-title', "messages[]" => :text process 'td.content>span.meta>a>abbr.published', "times[]" => "@title" result :messages, :times end.scrape(html, :parser_options => {:char_encoding => 'utf8'}) items end def getNextLink(html) link = Scraper.define do process 'div.pagination>a', :url => "@href", :kind => :text result :url, :kind end.scrape(html, :parser_options => {:char_encoding => 'utf8'}) link[:kind] =~ /Older/ ? link[:url] : nil end def getPageArchive(page) req = Net::HTTP::Get.new(page) req.basic_auth(USERNAME , PASSWORD) res = @http.request(req) res ? res.body : "" end end if $0 == __FILE__ keyword = nil pagenum = -1 use_brace = false opt = OptionParser.new opt.banner = "\nUsage: #{$0} -k KEYWORD -s STOPOLDERPAGE\n ex) #{$0} -k vim -p 10" opt.on('-k', '--keyword KEYWORD', String) {|k| keyword = k } opt.on('-p', '--pagenum PAGENUM', Integer) {|p| pagenum = p if p > 0 } opt.on('-l', '--logfile LOGFILE', String) {|l| logfilename = l } opt.on('-b', '--brace') {|b| use_brace = true if keyword } def opt.error(msg = nil) $stderr.puts msg if msg $stderr.puts help() exit 1 end begin opt.parse! rescue OptionParser::ParseError => err opt.error err.message end puts "Keyword => " + (keyword ? use_brace ? "[#{keyword}]" : keyword : "") puts "PageNum => #{pagenum}" puts "LogFile => #{logfilename}" taf = TwitterArchiveFilter.new(keyword, pagenum, use_brace) taf.filter() taf.dump(logfilename) puts "Succeed Twitter Archive!!" end
追記
バッファリングの遅延に対処するために puts を再定義していた部分を
「$stdout.sync = true」に置き換えた。