読者です 読者をやめる 読者になる 読者になる

FutureInsight.info

AI、ビッグデータ、ライフサイエンス、テクノロジービッグプレイヤーの動向、これからの働き方などの「未来」に注目して考察するブログです。

PythonでTwitter検索を係り受け解析しランキング表示する方法

興味があると何人かの方に言われたので以下の発表でランキング作成に利用したスクリプトを公開します。

動かし方

動かし方は以下の通りです。エンジニアなら簡単ですが、非エンジニアだと結構大変です。
(1) Python2.7以上をインスール(simplejsonがあればPython2.5以上でOK)Macなら最初から入っているはず。

(2) 以下のURLからYahoo Developer IDを取得してソースコードのYAHOO_DEV_IDに記載。

(3) 本エントリーの末尾にあるソースコードをUTF_8のファイル(ここでは仮にtwitter_jlp.py)として保存。
(4) ソースコード(twitter_jlp.py)のSETTING_TIMES、SETTING_KEYWORDを自分の解析用に変更
(5) 以下のコマンドプロンプトを実行。サーバに負荷をかけないようにスリープをいれているので時間かかります。

# macなら
> python twitter_jlp.py > ranking.txt
# winなら
> C:\Python27\python.exe twitter_jlp.py > ranking.txt

このスクリプトはYahoo係り受け解析APIとTwitter検索を利用しています。スリープはいれていますが、負荷には充分に注意してください。

APIをご提供の開発者の皆様に感謝します。

課題

こちらはかなり適当に書いたスクリプトですが、まじで解析しようとしたら以下の問題が発生する可能性があります。

  • Twitterの構文(@とかRTとか)を考慮してテキストをいじってから Yahoo係り受け解析APIに渡した方がよいでしょう。
  • 単純に検索結果を解析に食わしているだけなので、スパムとか非公式RTに弱いです。
  • 「○○はオワコン」の最後の名詞となる○○を抽出しています。○○が複数ある場合は最後の○○が対象です。他の構文も利用した方が良い結果を得られると思います。以下の本などを参考にanalyze_jlp_xml関数をいじればよいです。

入門 自然言語処理入門 自然言語処理
Steven Bird Ewan Klein Edward Loper 萩原 正人

オライリージャパン 2010-11-11
売り上げランキング : 15495

Amazonで詳しく見る
by G-Tools

ソースコード

# -*- coding: utf-8 -*-

import urllib, sys, time, os
from xml.etree import ElementTree

try:
    import simplejson as json
except ImportError:
    import json

# 設定値
# SETTING_TIMES: Twitter検索のページ数。
#                1ページに30件のつぶやきが含まれ最大300(9000件のつぶやき)。
#                必ず最初は1で動作することを確認してから行ってください
# SETTING_KEYWORD: 解析対象のキーワード。頭のuは必要ですので消さないでください。
SETTING_TIMES = 1
SETTING_KEYWORD = u"オワコン"

# Yahoo Developer IDは自分で取得してね!
YAHOO_DEV_ID = ""

# 解析に利用するURL
YAHOO_JLP = "http://jlp.yahooapis.jp/DAService/V1/parse?appid=%s&sentence="%YAHOO_DEV_ID
TWITTER_SEARCH_URL=u"http://yats-data.com/yats/search?"

# メイン関数
def main():
  download_twitter_data( SETTING_KEYWORD, SETTING_TIMES )
  download_jlp_xml( SETTING_KEYWORD, SETTING_TIMES )
  analyze( SETTING_KEYWORD, SETTING_TIMES )

# Twitter検索からデータをダウンロード
def download_twitter_data(keyword, times):
  if not os.path.isdir("twitter_data"):
    os.mkdir("twitter_data")

  for i in xrange(times):
    query = {
      "query":keyword.encode('utf-8'),
      "page":i
    }
    urllib.urlretrieve(TWITTER_SEARCH_URL+urllib.urlencode(query)+"&json", "twitter_data\\%s_%s.txt"%(keyword, i))
    time.sleep(1.0) # Sleepは多めに1秒いれる。この値は変えないでください!

# Yahoo係り受け解析APIの結果からデータをダウンロード
def download_jlp_xml(keyword, times):
  if not os.path.isdir("jlp_xml"):
    os.mkdir("jlp_xml")

  for i in xrange(times):
    contents = json.load( open( "twitter_data\\%s_%s.txt"%(keyword, i) ) )
    for content in contents:
      urllib.urlretrieve(YAHOO_JLP + urllib.quote(content["content"].encode('utf-8')), "jlp_xml\\%s.xml"%content["id"])

# 解析とデータ出力
def analyze(keyword, times):
  result = []
  result_set = set([])
  result_dict = {}
  for i in xrange(times):
    contents = json.load( open( "twitter_data\\%s_%s.txt"%(keyword, i) ) )
    for content in contents:
      jlp_xml = get_jlp_xml(content["content"], content["id"])
      target = analyze_jlp_xml(jlp_xml, keyword)
      if target != "" and target != None :
        result.append(target)
        result_set.add(target)
        if not result_dict.has_key(target):
          result_dict[target] = []
        result_dict[target].append((content["content"], content["url"]))

  ranking = []
  for i in result_set:
    ranking.append( (i, result.count(i)) )
  
  def compare(x, y):
    if x[1]>y[1]:
      return -1
    if x[1]<y[1]:
       return 1
    else:
       return 0
  ranking.sort(compare)
  
  # 表示するフォーマットはここで調節
  rank = 1
  for i in ranking:
    log( u"rank %s: %s point - %s"%(rank, i[1], i[0]) )
    rank = rank + 1
    for j in result_dict[i[0]]:
      log( u"%s %s"%(j[0] , j[1]) )
    log( "" )

# Yahoo 係り受け解析APIの結果を保存したファイルから取得
def get_jlp_xml(text, id):
  filehandle = open( "jlp_xml\\%s.xml"%id, "r")
  response_text = "".join( filehandle.readlines() )
  urllib.unquote(response_text).decode('utf-8')
  return  urllib.unquote(response_text).decode('utf-8')

# XMLはYahoo 係り受け解析APIを前提に解析
def analyze_jlp_xml(jlp_xml, keyword):
  dom = ElementTree.fromstring(jlp_xml.encode("utf-8"))
  chunk_items = dom.findall(".//{urn:yahoo:jp:jlp:DAService}Chunk")
  target_id = -1
  
  for chunk_item in chunk_items:
    morphem_items = chunk_item.findall(".//{urn:yahoo:jp:jlp:DAService}Morphem")
    for morphem_item in morphem_items:
      if morphem_item.find("{urn:yahoo:jp:jlp:DAService}Surface").text == keyword:
        target_id = chunk_item.find("{urn:yahoo:jp:jlp:DAService}Id").text

  if target_id != -1:
    text = ""
    for chunk_item in chunk_items:
      if chunk_item.find("{urn:yahoo:jp:jlp:DAService}Dependency").text == target_id:
        morphem_items = chunk_item.findall(".//{urn:yahoo:jp:jlp:DAService}Morphem")
        text = ""
        tmp = ""
        for morphem_item in morphem_items:
          if morphem_item.find("{urn:yahoo:jp:jlp:DAService}POS").text == u"名詞":
            text = text + morphem_item.find("{urn:yahoo:jp:jlp:DAService}Surface").text
          tmp =  tmp + morphem_item.find("{urn:yahoo:jp:jlp:DAService}Surface").text  + "(%s)"%morphem_item.find("{urn:yahoo:jp:jlp:DAService}POS").text
    return text

# log出力関数
def log(text):
  print text.encode( sys.getfilesystemencoding() )

if __name__ == '__main__':
  main()

出力結果

SETTING_TIMESに10、SETTING_KEYWORDにオワコンを指定した結果が以下のとおりです。

それではお楽しみください。