Hatena::Groupbugrammer

蟲!虫!蟲!

Esehara Profile Site (by Heroku) / Github / bookable.jp (My Service)
過去の記事一覧はこちら

なにかあったら「えせはら あっと Gmail」まで送って頂ければ幸いです。
株式会社マリーチでは、Pythonやdjango、また自然言語処理を使ったお仕事を探しています

 | 

2011-10-27

[]自分で記事を読むのもだるくなってきたので、コンピューターに記事を読ませることにした。 00:19

 そういえば、phaさんが"Tumblr bot"みたいなのを作っていて、「あー、TumblrBotを動かすという発想、あんまり考えたことがなかったなー」と思ったので、自分で記事を読むのもかったるいなーと思う記事を勝手に一文一文の重要度を作ってくれて、一文で重要な部分だけを抜き出してくれるような奴を作った。マルコフ連鎖でワードサラダ出来る奴でもよかったんだけど、それは散々Twitterでやられたので、さすがにお腹いっぱいだろ、ということで考えなかった。

仮説

 単純な仮説というか、示唆として「人がTumblrしているもののエントリから、形態素解析を行い、単語の頻度が高い一文を引用してくればいいのではないか」という風に考えてみる。辞書は基本的にテキストの引用が多いTumblrから作ってみた。具体的には、下のTubmlr。

 303 See Other

 ls@usada’s Morgue

 理由は端的にテキストが多いよね、という理由なので何も考えていない。単語帳はMeCabを使っている。Mecabってなんじゃらほい、という人はここの説明を読めばよろしいのではないかと。

 Error 404 (Page not found)!!1

 Ubuntuだと、辞書の文字エンコードの関係上、文字化けすることがあるので、下を見て対処するといいかもしれない。

 no title

 形態素解析ってなんじゃらほい、って簡単に説明すれば、単語がどういう属性なのかを調べてくれるパーサー、という言い方をしたらのいいのかな。例えば「僕は寂しい」だったら「僕/は/寂しい」と解析したりしてくれる。

 本当は、下のサンプルはあんまりよろしくない。その理由はJavaScript部分も形態素解析しちゃうから。

##DictoMake.py

import MeCab
import lxml.html
import urllib2
# --- Define

mecab = MeCab.Tagger("mecabrc")
words = {}

# --- Read Page
def MecabRead(url):
    html = urllib2.urlopen(url).read()
    dom = lxml.html.fromstring(html)
    page_data = lxml.html.tostring(dom,method ="text",encoding="utf-8")
    node = mecab.parseToNode(page_data)
    while node:
        word = node.surface
        if node.posid >= 36 and node.posid <= 67:
            if not words.has_key(word):
                words[word] = 0
            words[word] += 1
        node = node.next

def main():
    for p in range(1,1700):
        url = "http://rpm99.tumblr.com/page/" + str(p)
        MecabRead(url)
    for p in range(1,800):
        url = "http://shibata616.tumblr.com/page/" + str(p)
        MecabRead(url)
    for word,count in sorted(words.items(),key = lambda x:x[1]):
        print word,count

if __name__ == "__main__":
    main()

 辞書はこんな感じでできるはず。

…
…
…
浪人 19
ホラー 19
進学 19
電撃 19
立ち読み 19
ウィキペディア 19
リターン 19
カツマー 19
ユース 19
チヤ 19
多発 19
…
…
…

Lxml.htmlのほうがいいのか、BeautifulSoupのほうがいいのか

 上記では、lxmlというモジュールを使っている。というのは、lexmlは上でもやっているように、一回fromStringでDom解析をやったあとに、toStringを使うとテキストだけを吐き出してくれるようなことをやっているからだ。詳しくは下を見ると参考になる。

 Python(lxml)でhtmlを処理する まとめ - Gentleちゃれんじ Tips

 速さとしてはlxmlが抜群にいいという噂なんだけど、どうもうまくパースできないサイトもあるらしく、テキストが無いと判断してしまうことも、たびたびあったので、困ったなーとか考えていると、BeautifulSoupのやり方が非常に参考になった。

  BeautifulSoupでタグを除去してテキストだけ抜き出す - yanbe.py - pythonグループ

 で、二つを組み合わせて出来たスクリプトがこんな感じ。

# -*- coding:utf-8 -*-
import sys
import urllib2
import lxml.html
import re
from  BeautifulSoup import BeautifulSoup,NavigableString,Declaration,Comment
import pumblr
dicto = {}
argvs = sys.argv

def DictoRead():
    lines = open("dicto.txt","r")
    for line in lines:
        line = line.rstrip()
        key,value = line.split(" ")
        dicto[key] = value
    lines.close()

def getNavigableStrings(soup):
  if isinstance(soup, NavigableString):
    if type(soup) not in (Comment, Declaration) and soup.strip():
      yield soup
  elif soup.name not in ('script', 'style'):
    for c in soup.contents:
      for g in getNavigableStrings(c):
        yield g

def TextSearch(url):
    text = {}
    text_max = 0
    print "--- [[[ BeautifulSoup ]]] ---"
    soup = BeautifulSoup(urllib2.urlopen(url)) 
    print "--- [[[ SPLIT ]]] ---"
    text_list = "\n".join(getNavigableStrings(soup)).encode("utf-8").split("\n")
    print text_list
    print "--- [[[ Text Search ]]] ---"
    for i in text_list:
        if len(i) > 125:
            score = 0
            for key,value in dicto.items():
                try:
                    if re.compile(".*" + key + ".*").match(i) != None:
                        score = score + 1
                except KeyboardInterrupt:
                    exit()
                except:
                    continue
            text[i.lstrip()] = score
            print i
            print "SCORE :: " + str(score)
    return sorted(text.items(),key = lambda x:x[1],reverse=True)[0]

def main():
    if len(argvs) != 2:
        print "Usage : python myself.py [url]"
        exit()
    DictoRead()
    result = TextSearch(argvs[1])
    print result[0]
    pumblr.api.auth(email="FIXME",password="FIXME")
    pumblr.api.write_quote(quote=result[0],source=argvs[1])

if __name__ == "__main__": main()

 苦労したのは、テキストのエンコーディング周り。Pythonの日本語周りは、普通というか、それなりには文句がでる類で、他のサイトをパースしようとすると、いっつも文字化けになったりしていたのが大変だった。とりあえず、その辺りを完全対応させるのは、ひとまず諦めて、サクッと作ってみる。単語が入っているか否かは、基本的にはre.matchで、None以外のオブジェクトが帰ってきたら、何かしらにマッチしていると考えてPostするようにしている。pumblrはPythonTumblr投稿用モジュール。

 PythonでTumblrのライブラリを書いた - きちめも

 で、上記のスクリプトを走らせて、下のエントリを評価してみる。

 ニートのための世界史入門(1) - phaの日記

 f:id:nisemono_san:20111027002909p:image

 評価中。

 すると、このスクリプトが返してくれた、もっとも重要な文章はした。

今しか知らないと比較するものがないので今のこの状況を当たり前で当然のものとして考えてしまいがちだけど、歴史を知ることで「今の状況は過去の一つの選択肢の結果でしかない」と相対化して距離をとって考えることができる。また、現在は過去の一つの選択肢の結果でしかないけれど、それでも今の状態が成立したのはある程度の必然性があるわけで、現状を1か0で肯定/否定するのではなく、認める部分は認めて変えるべき部分は変えよう、といった柔軟な判断をするのにも歴史的経緯を知ることは助けになる。「銃・病原菌・鉄」のように1万3000年も遡る体力はないけれど、とりあえず200年ほど前に西欧で起きた「近代の始まり」まで遡ってみたい。「近代の始まり」とは今の社会システムの基礎が作られた地点だからだ。

 埋葬記 - 今しか知らないと比較するものがないので今のこの状況を当たり前で当然のものとして考えてしまいがちだけど、...

 または

(無職・20歳)とPS2で「サムライスピリッツ天草降臨」「Capcom VS SNK2」「King of Fighters 2002」などの古い格闘ゲームで対戦ばっかりしていたんだけど、これらは全てゲーム屋で500円とか1000円くらいで買えるものだ。1000円なんてパチスロでも打ったら10分で消えるお金だけど、その1000円で2人の人間が何十時間も飽きもせず遊んでいるわけで、ちょっとコストパフォーマンスが良すぎるのではないかと不安になる。

 埋葬記 - (無職・20歳)とPS2で「サムライスピリッツ天草降臨」「Capcom VS SNK2」「King...

 それなりに面白そうな部分を拾ってくれているみたいなので、とりあえず満足した。

 |