2008-05-17
■ [Ruby]Tumblrのpostにタグ付けするRubyスクリプト 
現在は使えません。
post内容に一部の文字(「”」、「β」など)が含まれている場合に、その文字を「?」に書き変えてしまうバグが見つかりました。このスクリプトは使用しないでください。→修正しました。Hpricotは&、"、<、>以外の実体参照を皆殺しにするみたいです。→直ってませんでした。→修正しました。正規表現を複数行モードにするのを忘れていた箇所がありました。
http://cxx.tumblr.com/tagged/quote
のようなURIで特定のタグが付いたpostにアクセスできるようになったので、とりあえずpost typeとfirst post / reblogだけタグ付けするためのスクリプトをRubyで書きました(Ruby 1.8.6 + WWW::Mechanize 0.7.6)。Rubyは詳しくないのでUTF-8じゃない環境で動くのかどうか分かってないです(追記:素のRuby 1.8.6-p114-i386-mswin32での動作を確認しました。日本語のタグを付けたければなんかしないといけないと思います。)。1 post当たり14秒ぐらいかかってるのですが、俺のTumblrは約10000 postsなので全部完了するのに2日ほどかかります(今も走らせてる)。アホか。
途中で中断してしまった場合でも
page = dashboard.filter('everything', 'me')
を
page = dashboard.get('http://www.tumblr.com/show/everything/by/me/100')
などと置き換えれば途中から再開できます。タグ付けの最中に通常通りTumblrを使用しても問題ありません。ただし、他のブラウザでのログイン操作を合計5回以上行なうと強制的にログアウトされると思います。このスクリプトを使用して何か問題が起きても責任は取れません。
2008-05-19
時間帯によっては1件当たり3〜5秒と、比較的実用的な速度で動いています。現在のコードは定期実行に適さず、あんまり意味がないのでもう少し書き直します。
2008-05-20
定期実行ができるように、既にタグを追加済みのpostに到達した時点で終了するようにしました。途中から再開する場合には
ruby tagging.rb [post ID]
として起動してください。最初のpostに限ってはタグが追加済みでも終了しません。深いページから再開する場合には
page = dashboard.filter('everything', 'me')
を
page = dashboard.get('http://www.tumblr.com/show/everything/by/me/100')
などに置き換えてください(書き換えた場合でも[post ID]を忘れるとすぐに終了してしまいます)。
2008-05-20
Post.idというメソッド名はまずかったのでPost.post_idにしました。
#!/usr/bin/ruby
require 'rubygems'
require 'mechanize'
email = ''
password = ''
module Tumblr
class Dashboard
PREFIX = 'http://www.tumblr.com'
def initialize(email, password)
@mech = WWW::Mechanize.new
login(email, password)
end
def login(email, password)
@emall = email
@password = password
doc = @mech.get('http://www.tumblr.com/login')
form = doc.forms.first
form.email = email
form.password = password
@mech.submit(form)
end
def logout
@mech.get('http://www.tumblr.com/logout')
end
def get(uri)
doc = @mech.get(uri)
Page.new(doc, @mech)
end
def page(pos=1)
get("http://www.tumblr.com/dashboard/#{pos}")
end
def filter(type='everything', author='everyone', pos=1)
get("http://www.tumblr.com/show/#{type}/by/#{author}/#{pos}")
end
class Page
attr_reader :posts
def initialize(doc, mech)
@doc = doc
@mech = mech
@posts = doc.search('#posts/li[@id]').collect do |post|
Post.new(post, mech)
end
end
def older
elem = @doc.at('#pagination/a[@title="Go to older posts"]')
elem ? Page.new(@mech.get(elem.attributes['href']), @mech) : nil
end
def newer
elem = @doc.at('#pagination/a[@title="Go to older posts"]')
elem ? Page.new(@mech.get(elem.attributes['href']), @mech) : nil
end
def older_link
elem = @doc.at('#pagination/a[@title="Go to older posts"]')
elem ? PREFIX + elem.attributes['href'] : nil
end
def newer_link
elem = @doc.at('#pagination/a[@title="Go to newer posts"]')
elem ? PREFIX + elem.attributes['href'] : nil
end
end
class Post
def initialize(elem, mech)
@elem = elem
@mech = mech
end
def post_id
@elem.attributes['id'] =~ /post(\d*)/
$1.to_i
end
def post_type
@elem.attributes['class'] =~ /regular|photo|quote|link|conversation|video|audio/
case $&
when 'regular': 'text'
when 'conversation': 'chat'
else $&
end
end
def is_reblog?
(@elem.attributes['class'] =~ /is_reblog/) ? true : false
end
def edit
elem = @elem.at('//a[@class="edit_link"]')
elem ? EditForm.new(@mech.get(elem.attributes['href']), @mech) : nil
end
def edit_link
elem = @elem.at('//a[@class="edit_link"]')
if elem
elem.attributes['href'] =~ /\?/
PREFIX + $`
else
nil
end
end
end
class EditForm
def initialize(doc, mech)
@doc = doc
@mech = mech
@form = doc.forms.first
end
def submit
@mech.submit(@form)
end
def tags
csv = @form['post[tags]']
(csv == 'comma, separated, tags') ? [] : csv.split(/\s*,\s*/)
end
def tags=(new_tags)
@form['post[tags]'] = new_tags.join(', ')
end
end
end
end
module Hpricot
def Hpricot.uxs(str)
str.to_s.
gsub(/&(\w+);/) { [NamedCharacters[$1]].pack('U') }.
gsub(/&#(\d+);/) { [$1.to_i].pack('U') }
end
end
dashboard = Tumblr::Dashboard.new(email, password)
page = dashboard.filter('everything', 'me')
last = page.posts.first.post_id + 1
first = ARGV[0] ? ARGV[0].to_i : page.posts.first.post_id
begin
loop do
page.posts.each do |post|
next if post.post_id > first or post.post_id >= last
last = post.post_id
new_tags = [post.post_type, post.is_reblog? ? 'reblog' : 'first post']
edit = post.edit
sum = edit.tags | new_tags
if sum == edit.tags
if last == first
next
else
exit
end
end
edit.tags = sum
edit.submit
end
break unless page.older_link
puts page.older_link
page = page.older
end
rescue
$stderr.puts "exception at post id #{last}: type 'ruby tagging.rb #{last}' to resume"
end
コメント