技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

Blog からいい感じに名詞を抽出したい

オライリー先生の課題で Blog ごとの比較とクラスタリングをするという例題があって、例題が英語だったので、日本語で対応しようとがんばってた。

日本語というだけで一気に難易度が上がる。

英語圏は空白でパースするだけで済む筈ですが、日本語だと形態素解析を実行しなければならない所が大分骨ではあります。

とりあえず RSS 拾っていい感じに形態素解析することまで。

libraryDependencies ++= Seq(
  "net.white-azalea" %% "scala_curl" % "0.1",
  "rome" % "rome" % "1.0"
)

を指定してほげっと

import java.io.StringReader
import net.reduls.igo.Morpheme
import scala.collection.JavaConversions._

object Utils {
  import net.azalea.curl._

  implicit val config = HTTP.options

  import com.sun.syndication.io.SyndFeedInput
  import com.sun.syndication.feed.synd.SyndEntry

  case class Entry()
  case class Feed(title: String, url: String, uri: String, entries:String)

  object Feed {
    def apply(rss: String):Feed = {
      val input = new SyndFeedInput
      val feed  = input.build(new StringReader(rss))

      val allEntries = feed.getEntries.map(o => o.asInstanceOf[SyndEntry]).map(s => s.getDescription.getValue).toList.mkString
      new Feed(feed.getTitle(), feed.getLink(), feed.getUri(), allEntries)
    }
  }

  def getRss(url: String) = {
    val result = HTTP.get(url).bodyAsString("UTF-8")
    Feed(result)
  }

  def parse(message: String):List[Morpheme] = {
    import net.reduls.igo.Tagger
    val tagger = new Tagger("ipadic")
    tagger.parse(message).toList
  }
}

object Cluster extends App {
  val rsses = List(
    "http://white-azalea.hatenablog.jp/rss"
  )

  def tagFilter(base: String) = {
    base.replaceAll("</?[a-zA-Z0-9]+>", "")
      .replaceAll("<[a-zA-Z0-9]+ [a-z]+=\".*?\">", "")
  }

  def contentFilter(tgt: Morpheme) = {
    tgt.feature.startsWith("名詞") &&
      !tgt.surface.isEmpty &&
      !tgt.surface.matches("^[!#$%&'()@0-9?_.,/:;{}<>=\\-\\[\\]\"+*¥[¥]。]+$") &&
      !tgt.surface.matches("^[a-zA-Z]$") &&
      !tgt.surface.matches("^[0-9]+$")
  }

  def count(values: collection.mutable.Map[String, Int], key: String) = {
    values.get(key).map(i => {
      values.put(key, (i + 1))
    }).getOrElse(
      values.put(key, 1)
    )
    values
  }

  val wordCounts = rsses.map(Utils.getRss).map(v => {
    val tokens   = Utils.parse(tagFilter(v.entries)).filter(contentFilter).map(v => v.surface)
    val mutables = collection.mutable.Map.empty[String, Int]
    val result   = tokens.foldLeft(mutables)((m, key) => count(m, key))
    println(v.url)
    v.url -> result.toMap
  })

  println(wordCounts)
}

タグを除去するとか、意味の無い変数名(アルファベット1文字)とか、数字のみの文字列を除去。