MeCab(形態素解析)を使ってTwitterプロフィールの頻出単語ランキングサービスを作ってみた。

##はじめに

自分のフォロワーのプロフィール文章を集めて、使用されている単語を形態素解析したら、フォロワーが今何に関心があるのかわかるんじゃないかと思い、MeCabの勉強も兼ねてサービスを作ってみました。

完成したサービスはこちらです。
(フォロワーが800人以上のアカウントではTwitterAPIとサーバーの影響で使用できません。すいません。)
https://twitter-mecab.herokuapp.com/

下の画像部分を形態素解析しています。
![custom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441198/cb6479f7-8ebc-e006-c405-32d1e2c5e009.png)

下記URL先の記事を参考に作成しました。
https://www.esz.co.jp/blog/152.html

##MeCabとは
MeCabとは文法や単語の品詞情報をもとに，文章を形態素(単語が意味を持つ最小の単位)に分解する解析(形態素解析)を行うことができるオープンソースの形態素解析器です。
今回はMeCabを使ってTwitterのプロフィール文章を形態素解析していきます。

##実行環境
- MacOS Mojave 10.14
- Ruby 2.5.1
- Ruby on Rails 5.2.3
- MeCab 0.996
- natto 1.1.2
- Twitter 6.2.0


##MeCabとnattoのインストール
今回はフレームワークにRailsを使って作成していきます。

まず、MeCabをHomebrewを使ってMeCabと辞書をローカル上にインストールします。

```
brew install mecab mecab-ipadic
```

次にnattoをインストールします
nattoはMeCabをRails上で簡単に使えるようにしてくれるGemです。
別にnattoを使用しなくてもMecabは使えますが、nattoを使った方が何かと便利です。

```ruby:Gemfile
  gem 'natto'
```
Gemfileにnattoを入れたら忘れずbundle installしてください。

MeCabと一緒にインストールしたmecab-ipadicという辞書を使用するためにパスを通します。

```
export MECAB_PATH=/usr/local/Cellar/mecab/0.996/lib/libmecab.dylib
```
これでMeCabとnattoを使う準備ができました！

##TwitterAPIを使ってプロフィールの文章を取得する

次にTwitterAPIを使えるようにするため、gemのtwitterをインストールします。

```ruby:Gemfile
  gem 'twitter'
```
bundle installも忘れずに行います。

調べたいTwitterIDを入力してもらうフォームは以下の通りです。

```erb:new.html.erb
     <div class="twiter-form">
        <%= form_with model: @page, :url => follower_analysis_pages_path, method: :get do |f| %>
            <%= f.text_field :follower_id, required: "true", class: "form-control" %>
            <%= f.submit "調べる", class: "btn btn-info" %>
        <% end %>
     </div>
```
コントローラーにfollower_idを送ります。

続いてコントローラーは以下の通りです。

```ruby:pages_controller.rb
  def new
    @page = Page.new
  end

  def follower_analysis

    #TwitterAPIを利用する
    client = Twitter::REST::Client.new do |config|
      config.consumer_key = ENV['TWITTER_CONSUMER_KEY']
      config.consumer_secret = ENV['TWITTER_CONSUMER_SECRET']
      config.access_token = ENV['TWITTER_ACCESS_TOKEN']
      config.access_token_secret = ENV['TWITTER_ACCESS_TOKEN_SECRET']
    end

    # フォロワーの数をまず取得(followers_countで取得できます。)
    user = client.user(page_params[:follower_id])
    @follower_count = user.followers_count

     # ユーザーの名前を取得(nameで取得できます)
    @my_name = user.name


   #TwitterAPI制限の関係で今回はフォロワー800人以上のアカウントは使用不可にします。
   if @follower_count > 800
     @limit_use = "フォロワーが800人以上いるアカウントは使用できません"
     return
   end

      begin

        #調べたいアカウントのフォロワーIDを全て取得
        follower_ids = client.follower_ids(page_params[:follower_id]).to_a

        #全てのidであるfollower_idsを100id毎(100idでAPIを1つ消費するため)の配列を作ってusersメソッドに渡す。
        #これにより100アカウント分のTwitter::Userの配列オブジェクトを取得する。これをconcatメソッドで全てまとめる。
        followers = follower_ids.each_slice(100).to_a.inject ([]) do |users, ids|
          users.concat(client.users(ids)) 
        end

        #nattoを使う
        @natto = Natto::MeCab.new

        #フォロワー全てのプロフィール文章を配列に格納(プロフィールはdescriptionで取得できる。)
        all_description = followers.map(&:description)

        #nattoで使えるようstringに変更
        @all_description = all_description.to_s

        #nattoのparseメソッドの引数に全てのプロフィール文章を入れて解析を行う。
        #今回は単語が２文字以上で、変な記号や単語は１つ１つ排除しています。
        #解析した結果、surfaceに文字列、featureに品詞などの情報が格納されています。
        surfaces = []
        @natto.parse(all_description_string) do |n|
          if n.feature.split(',')[0].in?(["名詞"]) && n.surface.size >= 2 && !n.surface.in?(['null','co', '://', 'https', 'http', '>,','こと', 'もの', 'of', 'and', 'on', 'in', 'the', 'to', 'ため', 'さん','好き', '大好き', 'お願い', 'アカウント', 'フォロー', 'こちら', 'DM', 'よう', '公式', 'ちゃん', 'くん', 'すき', '",'])
            #解析された全ての単語のみsurfaceに配列で格納
            surfaces << n.surface
          end
        end

        #surfaceに格納されている単語の中で同一の単語をまとめて、出現回数が多い順にソートする。
        words = surfaces.group_by(&:itself).map{|k, v| [k, v.size]}.sort_by{|k, v| -v}
        #出現回数が多い上位100単語を取得
        popular_words = words.first(100)

        #出現回数が多いトップ5の単語を取得
        @often_used_five_words = popular_words.first(5)

      
      #TwitterAPIの制限に引っかかってしまった際の処理
      rescue Twitter::Error::TooManyRequests => error

        @limit = error.rate_limit.limit #エラーを出したAPIの上限回数です。
        @remaining = error.rate_limit.remaining #エラーを出したAPIの残存回数です。エラーが出ているので、必ず0ですw
        @reset_at = error.rate_limit.reset_at #規制が解除される時刻です。

      end

      private

        def page_params
          params.require(:page).permit(:follower_id)
        end


  end
```

 #NEologdという辞書をNatto::MeCabクラスをnewする際使用すればおかしな記号等を１つ１つ排除する必要はなくなりますが、解析する速度がかなり遅くなったため、今回はNEologdは使用していません。



##解析結果をビューに表示します

```erb:follower_analysis.html.erb
<% if @limit_use.present? %>
    <h2><%= @limit_use %></h2>
    <%= link_to "検索ページに戻る", root_path, class: "btn btn-info" %><br>
<% else %>
      <h1>フォロワープロフィール頻出単語トップ5</h1>
      <!-- Chartkickを使用 -->
      <%= pie_chart @often_used_five_words %>

      <h2>もっと詳しいランキング</h2>
      <table class="result-table">
        <thead>
          <tr>
            <td>順位</td>
            <td>単語</td>
            <td>出現回数</td>
          </tr>
        </thead>
        <tbody>
          <% @popular_words.each.with_index(1) do |popular_word, i| %>
            <tr>
              <td class="">
                <%= "#{i}位" %>
              </td>
              <td class="">
                <%= popular_word[0] %>
              </td>
              <td class="">
                <%= "#{popular_word[1]}回" %>
              </td>
            </tr>
          <% end %>
        </tbody>
      </table>
<% end %>
```


##終わりに
MeCabを使って他にも面白いことができそうですね。
ツッコミどころの多いコードになっていますが、少しでもMeCabを使いたいと考えておられる方の参考になれば幸いです。