Hatena::Groupmkdir

作業ログとかをメモする場所。

 | 

2011-04-17

本名っぽい文字列を作る

| 13:04

この前「ネットで本名っぽい名前を名乗っておけば勝手に実名だと思ってもらえる」という話題になったときに、そういう名前を適当に名乗りまくったら面白いだろうなーと思ってそれっぽいシステムを作った。これを使ってTwitterのプロフィールとかを表記するようにしておけば、お人好しの県議会議員なんかがFollowしてくれるかもしれない。このシステムは便宜上、吉田海斗システムと呼ばれている。

2010-04-17 13:26追記

@tilyさんに教えてもらったruby-faker-japaneseが似たようなことするのに便利そう https://github.com/tily/ruby-faker-japanese

材料を揃える

苗字と名前のデータはWebからもってくる。「日本の苗字ランキング」みたいなサイトはあちこちにあるんだけど、クロールするのに向いていない構造だったり、JISコードにない文字が画像だったりして向かないサイトが多かった。結局、苗字は名字データベースから、名前は404 Not Foundからそれぞれクローラーを走らせて取得した。この2つのサイトはそれぞれ、漢字表記とひらがな表記の両方が載っていて、あとでいろいろいじくるのに適していた。ちなみに、「日本人のような名前」は詳しい説明がないのだけど、雰囲気的には読み方のランキングをもとに人名漢字を組み合わせているみたいで、漢字表記としては実在しないっぽい名前が多い。おもしろいのでそのまま使っている。

他には、こんなサイトを検討した

日本の苗字7000傑
IEじゃないと表示できないみたいなので諦めた。(TDC利用)
姓・名字・苗字の全国ランキング
IEじゃないと利用できないみたいなので諦めた。(TDC利用)
全国の苗字(名字)11万種
一覧が出せなさそうなので諦めた。
日本人の名字
JISコード外の文字が画像で表示されていて諦めた。
|| Not Found ||
クローラーは作ったけど読みが載っていないので使うのをやめた
同姓同名辞典(同姓同名検索・苗字ランキング・名前ランキング)
読みが載っていないので使わなかった

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 でいろんな表記がランダムで出るようになった。

Twitterで使う


#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 から聞いた。

mixiの名前を変更する

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直打ちで移動したほうが面倒が少ない。名前を変えまくることの利点としては、誰だかわから名前だと「だれだっけ」と思われて何度もクリックされたりするみたいで、あしあとが増えます。おもしろいですね。

 |