RとRubyで集合知プログラミングをするための準備

集合知プログラミングっぽいことををRejectセキュリティ&プログラミングキャンプでやろうかなーと思ってるんですが、データ操作とかは当日やりたくないのでぼちぼち準備とかしてみました。

apiとか使ってごにょごにょでもいいんですが、とりあえず自分が購読しているBlogをかたっぱしから集めてきて、MeCabで分解、分類みたいなことをやってみようかなーと思いました。そういうわけで

  • Rubyでスクレイピング
  • (Rubyにより)MeCabで名詞を抽出
  • Rで集計

というのをやっておきました。下拵えをやっておけば、たぶん面白いところに時間をかけられるはず。

単語の集計

こんな感じで指定したurlのrssに登場する単語数を数えてみました。単語とみなせないものは後でフィルタリングをかける。

> url <- "http://d.hatena.ne.jp/syou6162/rss2"
> word <- table(extract_meisi_from_blog(url))
> head(word[order(word,decreasing=TRUE)],30)

         .         ="          >          <          /       span      class 
       839        711        692        565        511        466        394 
        </          "        cgi          -          p     hatena          a 
       366        276        260        259        238        227        226 
       ://       http         ">         li          d         jp         ne 
       222        222        216        216        215        215        213 
        ><        img        src        bin        alt    mimetex synSpecial 
       183        136        136        134        133        130        130 
       tex          1 
       130        116 

で、opmlからblogのurlを引っ張ってきて、listにぶちこんでいます。

urls <- as.character(get_blogs_from_opml()$V2)
blogs_words <- list()

for(i in seq(urls)){
  blogs_words[[i]] <- table(extract_meisi_from_blog(urls[i]))
}

400個くらい購読しているBlogがあるんですが、その中の名詞は10万個くらい、という結果になりました。というわけで、400×100000くらいの行列を作れば、類似度とかその辺の計算はできるでしょう。

> length(names(unlist(blogs_words,recursive=FALSE)))
[1] 101926

でーたまにゅぴゅれーしょん疲れます。。。

スクレイピングとかのコード

今回はさすがにRubyで分けたほうがいいような気がしてきたんですが、そうしたら負けか。csvとかの中間ファイルはさすがに使ってもいいかなと思い始めた。よく分からないエラーで悩まされたりするし(textConnectionの付近)。

extract_meisi_from_blog <- function(url){
  ruby <- paste("
require %q[rubygems]
require %q[open-uri]
require %q[hpricot]
require %q[MeCab]
require %q[kconv]

url = %q[",url,"]

description = %q[]

begin
  doc = Hpricot(open(url).read)
rescue => ex
  exit
end

(doc/:item).each {|item|
  description = description + (item/:description).inner_text
}

begin 
  c = MeCab::Tagger.new(%q[-Ochasen]) 
  n = c.parseToNode(description) 

  list = Array.new
  while n do
    f = n.feature.split(/,/)
    if /名詞/ =~ f[0]
      list.push(n.surface)
    end
    n = n.next
  end 
  list.each{|x| puts x}
rescue
  exit
end
",sep="")
  return(system(paste("echo '",ruby,"'"," | /opt/local/bin/ruby ",sep=""),intern=TRUE))
}

get_blogs_from_opml <- function(){
  ruby <- paste("
# -*- coding: utf-8 -*-
#!/usr/bin/ruby -Ke

require %q[rubygems]
require %q[open-uri]
require %q[hpricot]
require %q[MeCab]
require %q[kconv]
require %q[csv]

opml = %q[/Users/yasuhisa/Downloads/google-reader-subscriptions.xml]
doc = Hpricot(open(opml).read)

blogs = []

(doc/:outline).each {|item|
  hash = Hash.new
  hash[:title] = item[:title] 
  hash[:url] = item[:xmlurl]
  blogs.push hash
}

blogs.reject!{|blog| blog[:url].nil?}

CSV::Writer.generate($stdout) do |writer|
  blogs.each{|blog|
    writer << [blog[:title],blog[:url]]
   }
end
",sep="")
  vec <- system(paste("echo '",ruby,"'"," | /opt/local/bin/ruby ",sep=""),intern=TRUE)
  text <- textConnection(vec)
  return(read.csv(text,header=FALSE))
  close(text)
}