ruby, mechanize, twitter, mixi |
この前「ネットで本名っぽい名前を名乗っておけば勝手に実名だと思ってもらえる」という話題になったときに、そういう名前を適当に名乗りまくったら面白いだろうなーと思ってそれっぽいシステムを作った。これを使ってTwitterのプロフィールとかを表記するようにしておけば、お人好しの県議会議員なんかがFollowしてくれるかもしれない。このシステムは便宜上、吉田海斗システムと呼ばれている。
@tilyさんに教えてもらったruby-faker-japaneseが似たようなことするのに便利そう https://github.com/tily/ruby-faker-japanese
苗字と名前のデータはWebからもってくる。「日本の苗字ランキング」みたいなサイトはあちこちにあるんだけど、クロールするのに向いていない構造だったり、JISコードにない文字が画像だったりして向かないサイトが多かった。結局、苗字は名字データベースから、名前は日本人のような名前 - 日本人のような名前 男性版からそれぞれクローラーを走らせて取得した。この2つのサイトはそれぞれ、漢字表記とひらがな表記の両方が載っていて、あとでいろいろいじくるのに適していた。ちなみに、「日本人のような名前」は詳しい説明がないのだけど、雰囲気的には読み方のランキングをもとに人名漢字を組み合わせているみたいで、漢字表記としては実在しないっぽい名前が多い。おもしろいのでそのまま使っている。
他には、こんなサイトを検討した
TDCを使用しているサイトに関してはIEを使うのがダルくてさいしょからあきらめていたけど、内部で使われているCSVファイルをダウンロードしていけるかもしれない、というのにさっき気づいた。
クローラーのコードをここに書いてもいいのだけど、それをするくらいならクロール済みデータを渡したほうが役に立ちそうだし、でもそういう再配布行為はさすがに怒られそうなのでここではやらないことにしておく。クローラーはいつもどおりMechanizeで作って、それをSequel経由でSQLite3に入力するようになっている。
苗字を取得したサイトでは、JISコード外の文字を「(はしご高)」のように表現してあったので、それを手作業で修正する必要があった。そこで、クロールするときに「(」を含むものについてはflagというカラムに1を、そうでないものには0をそれぞれ入力するようにしておいて、flagが1になっているものについてはあとで修正して使うようなかたちにした。
修正用のスクリプトはこんな感じになった。
#coding:utf-8 require 'kconv' require 'sequel' new_kanji = {} DB = Sequel.sqlite('yoshida.db') DB[:last].filter(:flag=>1).all.each do |r| r.each_pair do |k,v| puts "#{k} : #{v}" end puts "================" old_name = r[:name] new_name = old_name.gsub(/([^)]*)/) {|m| new_kanji[m]?new_kanji[m]:m} skip_flag = false if old_name != new_name puts "-> \"#{new_name}\"" puts "edit? [y/n]" while true if ARGF.gets.match(/([yn])/i) if $1 == 'y' break else DB[:last].filter(:id=>r[:id]).update(:name=>new_name,:flag=>0) skip_flag = true puts "updated" break end end end end next if skip_flag puts "input new name" input = ARGF.gets.toutf8.gsub(/\s/,'') if input.length == 0 puts "skip" else puts "================" puts "Are You hure? : #{input}" while true if ARGF.gets.match(/([yn])/i) if $1 == 'y' DB[:last].filter(:id=>r[:id]).update(:name=>input,:flag=>0) after = input.split('') before = old_name.gsub(/(([^)]*))/,',\\1,').split(',').map!{|m|m.match(/(([^)]*))/) ? m : m.split('')}.flatten before.each_index do |i| if before[i].match(/(([^)]*))/) new_kanji[before[i]] = after[i] puts "#{before[i]} => #{after[i]}" end end puts "updated" break else puts "skip" break end end end end puts "================" end
実行するとおかしいところのあるデータを表示してくれるので、入力できるやつは入力する。一度たとえば「(はしご高)」を「髙」に変更して入力すると以後「(はしご高)」を「髙」に変換するのを勝手にやってくれる。べんり。
データベースからランダムに値を抜き出す方法はなにかないか考えたときに、いちばん楽でそこそこ速いのはそれぞれのレコードに整数値でIDを振っておいて、乱数でそのIDのレコードを引き出すことだろうと思った。Sequelでは検索の結果が配列になるので、そこからsampleメソッドでランダムに貰っても良かったのだけど、メモリ確保にものすごく時間がかかるのでやめた。このへんはもっと賢い方法があるのかもしれない。
名前のデータに時々変な文字が入っていたりしたので、ひらがなカタカナ漢字でないものは外すようにした。変なモノがきたら再検索するようになってるけど、変なモノが来る確率は非常に低いので問題ない。
#coding:UTF-8 require 'sequel' require 'romankana' module YoshidaKaito DB = Sequel.sqlite(File.dirname(__FILE__) + '/../db/yoshida.db') def YoshidaKaito.choice type biggest = DB[type].reverse_order(:id).first[:id] while true last_name = DB[type].filter(:id => (biggest*rand).floor).first if last_name && last_name[:flag] == 0 && last_name[:name] =~ /^[ぁ-んァ-ヴ一-龠]*$/ return last_name end end end def YoshidaKaito.first YoshidaKaito.choice :first end def YoshidaKaito.last YoshidaKaito.choice :last end def YoshidaKaito.full {:first=>YoshidaKaito.first,:last=>YoshidaKaito.last} end end
これで、 YoshidaKaito.fullとかすれば、
{:first=>{:id=>664372, :name=>"祥暁", :yomi=>"やすあき", :flag=>0}, :last=>{:id=>39891, :name=>"峯川", :yomi=>"みねかわ", :flag=>0}}
みたいなデータが貰える。便利。
実名でTwitterを使ってる人でも、表記は漢字だったりひらがなだったりローマ字だったりカタカナだったりする。そこで、名前を作ったうえでいろんな表記に変形するようにした。ローマ字やカタカナ変換へのには、このあいだ作ったromankanaを使う(このために作った)。
表記方法は、Procオブジェクトの配列で表現して、苗字と名前のデータを引数としてランダムに選んだProcオブジェクトをcallすることで適当な表記に変更した文字列を貰えるようにする。
module YoshidaKaito MM = [ [ Proc.new {|f,l| "#{l[:name]} #{f[:name]}"}, Proc.new {|f,l| "#{l[:name]}#{f[:name]}"}, ], [ Proc.new {|f,l| "#{l[:yomi].to_hiragana} #{f[:yomi].to_hiragana}"}, Proc.new {|f,l| "#{l[:yomi].to_hiragana}#{f[:yomi].to_hiragana}"}, ], [ Proc.new {|f,l| "#{l[:yomi].to_katakana} #{f[:yomi].to_katakana}"}, Proc.new {|f,l| "#{l[:yomi].to_katakana}#{f[:yomi].to_katakana}"}, Proc.new {|f,l| "#{f[:yomi].to_katakana} #{l[:yomi].to_katakana}"}, Proc.new {|f,l| "#{f[:yomi].to_katakana}#{l[:yomi].to_katakana}"}, ], [ Proc.new {|f,l| "#{l[:yomi].to_roman} #{f[:yomi].to_roman}"}, Proc.new {|f,l| "#{l[:yomi].to_roman} #{f[:yomi].to_roman.upcase}"}, Proc.new {|f,l| "#{l[:yomi].to_roman.upcase} #{f[:yomi].to_roman.upcase}"}, Proc.new {|f,l| "#{l[:yomi].to_roman.capitalize} #{f[:yomi].to_roman.capitalize}"}, Proc.new {|f,l| "#{l[:yomi].to_roman.upcase} #{f[:yomi].to_roman.capitalize}"}, Proc.new {|f,l| "#{l[:yomi].to_roman.capitalize} #{f[:yomi].to_roman.upcase}"}, Proc.new {|l,f| "#{l[:yomi].to_roman} #{f[:yomi].to_roman}"}, Proc.new {|l,f| "#{l[:yomi].to_roman} #{f[:yomi].to_roman.upcase}"}, Proc.new {|l,f| "#{l[:yomi].to_roman.upcase} #{f[:yomi].to_roman.upcase}"}, Proc.new {|l,f| "#{l[:yomi].to_roman.capitalize} #{f[:yomi].to_roman.capitalize}"}, Proc.new {|l,f| "#{l[:yomi].to_roman.upcase} #{f[:yomi].to_roman.capitalize}"}, Proc.new {|l,f| "#{l[:yomi].to_roman.capitalize} #{f[:yomi].to_roman.upcase}"}, ], ] def YoshidaKaito.all names=YoshidaKaito.full "#{names[:last][:name]} #{names[:first][:name]}(#{names[:last][:yomi]} #{names[:first][:yomi]})" end def YoshidaKaito.normal names=YoshidaKaito.full "#{names[:last][:name]} #{names[:first][:name]}" end def YoshidaKaito.random names=YoshidaKaito.full MM.sample.sample.call(names[:first],names[:last]) end end
これで、YoshidaKaito.full でふつうの漢字表記、YoshidaKaito.normal でカッコで読み方を併記した表記、YoshidaKaito.random でいろんな表記がランダムで出るようになった。
#coding:utf-8 $LOAD_PATH << File.dirname(__FILE__) require 'lib/yoshida.rb' require 'twitter' Twitter.configure do |config| config.consumer_key = 'hogehoge' config.consumer_secret = 'fugafuga' config.oauth_token = 'mogemoge' config.oauth_token_secret = 'piyopiyo' end client = Twitter::Client.new names = YoshidaKaito.full name_for_twitter = YoshidaKaito.random(names) name_with_kana = YoshidaKaito.all(names) puts name_with_kana client.update_profile(:name=> name_for_twitter)
このスクリプトを定期的に走らせることで、Twitterの名前がどんどん変わっていく。たのしい。
かな併記の表記が出力されるようになっているのは、これを別のスクリプトにパイプして、そこでは別のTwitterアカウントが標準入力の文字列をpostするという処理になっている。こうして、あるアカウントで名前変更のログがひたすら発言されている状態になっている。こういうスクリプトがあると便利だよという話は、d:id:shokai から聞いた。
facebookでやると確実に許されないので、僕の中でオワコンと化している(まりもの水替えしかしていない)mixiの名前を変えまくるスクリプトを作った。
#coding:utf-8 $LOAD_PATH << File.dirname(__FILE__) require 'lib/yoshida.rb' require 'mechanize' require 'kconv' names = YoshidaKaito.full nick = "#{names[:last][:name]}#{names[:first][:name]}" agent = Mechanize.new agent.max_history = 1 agent.get('http://mixi.jp/') agent.page.form('login_form').field('email').value = 'dankogai@example.com' agent.page.form('login_form').field('password').value = 'kogaidan' agent.page.form('login_form').submit agent.get('http://mixi.jp/edit_profile.pl') agent.page.form('regForm').field('last_name').value = names[:last][:name] agent.page.form('regForm').field('first_name').value = names[:first][:name] agent.page.form('regForm').field('nickname').value = nick agent.page.form('regForm').submit agent.page.forms.first.submit
ログインしたあと、本当ならmetaタグリフレッシュにしたがってホーム画面に移動してリンクをクリックして……ということをやるのが普通だと思うのだけど、mixiの場合はURL直打ちで移動したほうが面倒が少ない。名前を変えまくることの利点としては、誰だかわから名前だと「だれだっけ」と思われて何度もクリックされたりするみたいで、あしあとが増えます。おもしろいですね。