Hatena::ブログ(Diary)

Subspace at Life

2011-04-20

RSSフィードから単一品詞の単語別出現数を取得してみた.

| 04:00

今回はOreilly集合知プログラミングの第3章を参考に

指定した日本語のRSSフィード形態素解析して,

単一品詞の単語別出現数を取得するPythonコードを作成してみました.

集合知プログラミング

集合知プログラミング

RSSフィードから,各記事のタイトル,リンク,エントリ等を取り出すのには

Universal Feed Parserというpythonモジュールを用います.

また,形態素解析には前回の記事で利用したMeCabを使います.

作成コード

日本語をcsvファイルへ出力する部分はココを参考にさせてもらいました.

また,extractKeyword関数csvモジュールについてはそれぞれ過去の記事(クリックすれば飛ぶ)で扱っています.

#coding:utf-8
import re
import csv
import MeCab
import feedparser
from extractKeyword  import extractKeyword

# //////////////////////
# ----- set URLs -----
# //////////////////////
URLs = ['http://news.livedoor.com/topics/rss/dom.xml',
        'http://dailynews.yahoo.co.jp/fc/rss.xml',
        'http://news.google.com/news?hl=ja&ned=us&ie=UTF-8&oe=UTF-8&output=atom&topic=h&hl=ja',
        'http://sankei.jp.msn.com/rss/news/points.xml']

# /////////////////////////////////
# ----- category selection -----
# /////////////////////////////////
class_num = 0
word_classes = [u'名詞',u'動詞',u'形容詞',u'副詞',u'助詞',u'助動詞']
word_class = word_classes[class_num]

# ///////////////////////////////
# ----- defined functions -----
# ///////////////////////////////
def getwordcounts(url):
    """RSSフィードのタイトルと、単語の頻度のディクショナリを返す"""
    # フィードをパースする
    d = feedparser.parse(url)
    wc={}
    
    # すべてのエントリをループする
    for e in d.entries:# RSSの種類によって,記事に相当する部分が異なることに対応する
        if 'summary' in e: summary = e.summary
        elif 'description' in e: summary = e.description
        else: summary = e.link

        # 単語のリストを取り出す
        words = getwords(e.title+' '+summary)
        # extractKeywordを用いるため,単語を連結してテキスト化
        txt = ''
        for i in range(len(words)):
            txt += words[i]
        words_sub = extractKeyword(txt,word_class)
        for word in words_sub:
            wc.setdefault(word,0)# dict型のkeyにwordがなければ,value=0として新設
            wc[word] += 1
    return d.feed.title,wc

def getwords(html):
    """すべてのHTMLタグを取り除き,単語リストを返す"""
    txt = re.compile(r'<[^>]+>').sub('',html)# htmlから'<[^>]+>'を''に置き換える
    tagger = MeCab.Tagger('-Owakati')
    return tagger.parse(str(txt)).split(' ')

def main():
    apcount = {}
    wordcounts = {}
    feedlist = [line for line in URLs]
    for feedurl in feedlist:
        wc = {}
        try:
            title,wc_ini = getwordcounts(feedurl)
            for w,bc in wc_ini.items():
                wc.setdefault(w.encode('sjis'),bc)# csvで日本語を表示させるため,Shift_JIS化
            wordcounts[title] = wc
            for word,count in wc.items():
                apcount.setdefault(word,0)
                if count>1:
                    apcount[word]+=1# それぞれの単語が出現するフィードの数を数える
        except:
            print 'Failed to parse feed %s' % feedurl# urlが認識できなければエラー表示
    
    # でたらめな単語が存在するフィードがあることや一般的な単語の除外を考慮し,単語の出現率に敷居値を設定する
    wordlist = []
    for w,bc in apcount.items():
        frac = float(bc)/len(feedlist)
        if frac>0.1 and frac<0.5: wordlist.append(w)
    
    # csvへの書き込み用リストを生成
    header = ['Feed_name']+wordlist
    rows = []
    for blog,wc in wordcounts.items():
        row = [blog.encode('sjis')]
        for word in wordlist:
            if word in wc: row.append(wc[word])
            else: row.append('0')
        rows.append(row)
    
    # csvへ書き込み
    csv_writer = csv.writer(open('rss_data.csv','w'),lineterminator='\n')
    csv_writer.writerow(header)
    csv_writer.writerows(rows)    
    
if __name__ == "__main__":
    main()

--

[注] csvへの書き込みようリストを生成する部分で

# wordlistのkeyはShift-JIS化済みで,wordcountのはutf-8形式として...
for blog,wc in wordcounts.items(): 
    for word in wordlist:
        if word in wc: row.append(wc[word].encode('sjis'))
        else: row.append('0')

だと'wc'がの日本語の場合,条件分岐'if word in wc:'において,その'else:'部分しか実行されなくなる.

出力結果(csvファイルの中身から抜粋)

上記のコードでは,各ニュースサイトRSSフィードから名詞を抽出しています.

Feed_name all 議長 雇用 野菜 原発 知事 大使館 公開 カジノ 職員 ...
livedoor NEWS - 国内トピックス 0 0 0 0 1 0 0 0 0 0 ...
Top Stories - Google News 8 0 4 3 13 3 0 4 0 4 ...
[注目] - MSN産経ニュース 0 3 0 0 0 0 2 0 2 0 ...
Yahoo!ニュース・トピックス - トップ 0 0 0 0 0 0 0 0 0 1 ...
トラックバック - http://d.hatena.ne.jp/r_e10/20110420/1303326055