断クリプト日記(n日ぶりm回目)

暗号通貨ガチホしつつbotトレード極めます

コイン〇ェックのチャットを感情分析によるテキストマイニングしてみた

どうもみなさん、ガチホしていますか?

私はBTC-FXのSFDが発動しているのでbotトレは少しお休みして、
短期分のアルトをころころ転がしています。

5月はどのぐらいお祭り相場になるのかはわかりませんが、
私は基本的に直近でリリースがある通貨が利確のタイミングを計りやすいので
仕手戦を転々としていこうかと思います。(もちろん余剰資金だけですが。。)

さて、つい数時間前に仮想通貨の春ならぬTwitterの春が私のもとに訪れまして、
ゴールデンウィークの自由研究としてやっていたコインチェックのチャット分析を
かの有名な仮想NISHIさんに取り上げていただきました。(ありがとうございます。)
気づけばフォロワーさんが数時間で2倍になりました。仮想通貨だったら利確したい

私自身はしがない技術好きなオタクの一人でしかなく、データサイエンスのプロではないのですが
予想以上に反響が大きく、また今後発展してほしい分野だと思っていますので
今後の発展に期待を込めて、現時点での成果物を共有させていただけたらと思います。

今回の概要

今回作るパッケージはこのような構成になります。
f:id:cryptocurrency_chudoku:20180430015739p:plain
簡単に説明しますと、以下の2フォルダ、2ファイルでできます。
 [フォルダ] Log_CoinCheck :ログの格納フォルダ
 [フォルダ] phantomjs-2.1.1-windowsGUIなしでwebにアクセスできるツール(※要ダウンロード)
 [module] EmotionAnalysisBot.py:テキストマイニング用ファイル
 [module] GetChatLog.py:コインチェックのチャットログ収集用ファイル
 [module] GetChatLog_Zaif.py:Zaifのチャットログ収集用ファイルですが、Zaifのチャットはプロトコルがwebsocketになっていて上手く接続できないので教えてください

コインチェックのチャットからログを収集しよう

コインチェック自体はチャットのAPIとかないのですが、
下記のサイト様を参考にチャットログ収集用のクラスを作ってみました。
[参考サイト]
【コインチェック】APIが存在しないはずのチャットの自動読み上げシステムを作る

GetChatLog.py

import requests
import time
import datetime
from selenium import webdriver


class EXEC_GET_CHAT_LOG:

    def get_coincheck(self):

        # pjs_pathは今回フルパスで指定していますが、phantomjsのフォルダの中で「bin/phantomjs」が指定できればOKです。
        pjs_path = 'C:/pleiades/workspace/DataScienceBot/EmotionAnalysisBot/phantomjs-2.1.1-windows/bin/phantomjs'
        access_url = 'https://coincheck.com/ja/chats'
        l_coin_session2 = ""
        l_ga = ""
        l_gid = ""
        driver=webdriver.PhantomJS(executable_path = pjs_path)


        # CoinCheckにアクセス
        driver.get(access_url)

        # クッキー情報の取得
        cookies = driver.get_cookies()

        # クッキーのチェック
        print("check cookies:")

        for c in cookies:
            if "name" in c and c["name"] == "_coin_session2":
                l_coin_session2 = c["value"]
            elif "name" in c and c["name"] == "_ga":
                l_ga = c["value"]
            elif "name" in c and c["name"] == "_gid:":
                l_gid = c["value"]




        while True:

            # APIのエンドポイント
            url = 'https://coincheck.com/api/chats/list'

            header = {'accept': 'application/json, text/plain, */*'
                    ,'accept-encoding': 'gzip, deflate, br'
                    ,'accept-language': 'ja,en-US;q=0.9,en;q=0.8'
                    ,'cookie': '_coin_session2=' + l_coin_session2 + '; _ga=' + l_ga + ';  _gid=' + l_gid
                    ,'referer': 'https://coincheck.com/ja/chats'
                    ,'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'
                    ,'x-csrf-token': ' ' # Chromeのコンソールから直接抜いてくるか、webdriverの処理で取得して代入してください。
                    ,'x-requested-with': 'XMLHttpRequest'
                      }


            # json形式で結果を受け取る
            response = requests.get(url, headers=header).json()

            # 現在の日付
            YYYYMMDD=datetime.date.today()

            # ファイル名の命名
            file_nm='Log_CoinCheck/' + str(YYYYMMDD) + '.txt'

            # ファイルOPEN
            f = open(file_nm, 'a',encoding='utf-8')

            print('**************************************************************************************************')
            # ログの出力
            for res in response['chats']:
                print("Time:{} message:    {}".format(res['created_at'] ,res['content'].replace('\n','')))
                f.write("Time:{} message:    {}".format(res['created_at'] ,res['content'].replace('\n','')))
                f.write("\n")

            f.close()
            # Sleep
            time.sleep(180)

        # ドライバーのクローズ
        driver.quit()





if __name__ == '__main__':
    # ロード
    exec_get_log = EXEC_GET_CHAT_LOG()

    # 実働
    exec_get_log.get_coincheck()


簡単に処理の内容だけ説明させていただきますと、
コインチェックにはチャット専用のURLがありまして、
そのページを開くと最新のチャットログを50件取得するようになっています。

そのページを開いたときにAPIが呼ばれているのですが、
この非公式APIを3分間隔で叩いてログを収集しています。
※2018/4/30現在では3分で50件ほどログが生成。ただし、多少抜けていても誤差の範疇だと思います。

以下はその実例です。GoogleChromeの開発者コンソール画面(F12)から内容を確認できます。

f:id:cryptocurrency_chudoku:20180430031638p:plain

また、PGでは上記画像のクッキーも自動で取得していますが、
「'x-csrf-token'」というパラメタが私の作成したファイルではハードコーディングになっていたので
PGで自動取得させるか、Chromeの画面から手動で抜いてきて、コーディングをお願いします。。

実際のログは下記のようなフォーマットで作成されます。(メッセージの前だけTAB区切り)
f:id:cryptocurrency_chudoku:20180430042632p:plain

テキストマイニングにかけてみよう


実際にデータをこねくりまわす部分です。
今回はGoogle Cloud Platformの感情分析APIというものを使ってみました。
一定回数以上APIを使うと課金制になりますが、無料である程度楽しめるようです。

こちらのサイト様を参考にさせていただきましたので、説明はこちらから引用とさせていただきます。
to-kei.net
※後日追記(下記のリンクもわかりやすいので、ご参考まで)

Natural Language API でエンティティと感情を分析する

EmotionAnalysisBot.py

import requests
import unicodedata


class EMOTIONA_ANALYSIS_BOT:

    def get_east_asian_width_count(self,text):

        count = 0
        # ifブロックは本来2byte以上の文字を計算するのに使うが、Googleがマルチバイト文字を1文字で換算するため同一処理で可
        for c in text:
            if unicodedata.east_asian_width(c) in 'FWA':
                count += 1
            else:
                count += 1
        return count


    def exec_read_text(self,file_nm):

        f = open(file_nm, 'r', encoding='utf-8')

        # 1行毎にファイル終端まで全て読む(改行文字も含まれる)
        lines = f.readlines()

        f.close()

        msg_list=[]
        for line in lines:
            # TAB区切りで文字列を分割し、メッセージ部分を改行コード抜きで配列に追加
            msg=line.split('    ')[1].replace('\n','')
            # 重複するログは除外
            if msg not in msg_list:
                msg_list.append(msg)


        # API連携用テキストレコードリスト
        api_text=[]
        str=""

        for now_msg in msg_list:

            # 1000文字分のテキストを作成
            if self.get_east_asian_width_count(str) + self.get_east_asian_width_count(now_msg) < 998:
                str += now_msg + "。"

            # 1000文字を超えた場合
            else:
                api_text.append(str)
                str = now_msg + "。"

        # 最終行をレコードに追加
        api_text.append(str)

        return api_text


    def exec_analysis_text(self,api_text):

        #APIキーを入力(Google Cloud Platformの画面から有効化したうえで入力)
        key = ""

        total_score=0
        total_magnitude=0
        all_text=""

        for analysis_text in api_text:

            # 感情分析したいテキスト
            text = analysis_text
            # テキストの総和
            all_text+=text

            #APIエンドポイント
            url = 'https://language.googleapis.com/v1/documents:analyzeSentiment?key=' + key

            #基本情報の設定 JAをENにすれば英語のテキストを解析可能
            header = {'Content-Type': 'application/json'}
            body = {
                "document": {
                    "type": "PLAIN_TEXT",
                    "language": "JA",
                    "content": text
                },
                "encodingType": "UTF8"
            }

            #json形式で結果を受け取る。
            response = requests.post(url, headers=header, json=body).json()

            total_magnitude+=float(response["documentSentiment"]["magnitude"])
            total_score+=float(response["documentSentiment"]["score"])

            #分析の結果をコンソール画面で見やすく表示
            print("**********************************総合magnitude**********************************")
            print(response["documentSentiment"]["magnitude"])
            print("**********************************総合score**********************************")
            print(response["documentSentiment"]["score"])
            for i in response["sentences"]:
                print("magnitude:",i["sentiment"]["magnitude"],"  score:",i["sentiment"]["score"],"  text:",i["text"]["content"])

        print("***************************************本日のtotalマグニチュード***************************************")
        print(total_magnitude)
        print("***************************************本日のtotalスコア***************************************")
        print(total_score)
        print("***************************************本日のtotal文字数***************************************")
        print(self.get_east_asian_width_count(all_text))

        return

if __name__ == '__main__':

    # ロード
    emotionmal_analysis = EMOTIONA_ANALYSIS_BOT()

    # 実行
    api_text=emotionmal_analysis.exec_read_text('Log_CoinCheck/2018-04-28.txt')

    # 解析
    emotionmal_analysis.exec_analysis_text(api_text)


APIの解析単位が1000文字だと思いますので、PG上では1000文字単位以内で
チャットのログを分割してAPIを実行するようにしています。
あとはトータルスコアなども出力するようにしていますが、バグっていたらすみませんorz

実行して普通に動かせば下記のような結果が出ると思います。
f:id:cryptocurrency_chudoku:20180430044757j:plain

まとめ

最後にまとめです。
現在は機械学習ディープラーニングなどのライブラリが非常に充実しているため、
プログラムさえ書ければ個人でもこのようにデータサイエンティスト気分を簡単に味わえます。

ただ実際のデータサイエンスの分野というのはここまでシンプルなものではなくて、
3段階に分けられるのではないかと思います。(1が難しく、3が易しい)

  1. モデルを構築できる
  2. パフォーマンスチューニングできる
  3. モデルを実装できる(使える)


今回実践したのは3~3.5というレベルなのですが、
この場合、使うモデル自体が間違っている・もしくはモデルが最適な結果を返してくれない場合、
意味のない結果しか返ってこずあまり役に立ちません。

ただ、1.ができる人というのはその道のプロであるためハードルはかなり高いですが、
そうでなくとも、2.ができる、もしくは3.ができる人でも複数のモデルを使いこなせることができれば
何か違う結果が見えてくるのかもしれません。

※ブログもPGも急ピッチで書いてしまったので、品質についてはご容赦下さいm(_ _)m

最後まで読んでいただきありがとうございました。