Help us understand the problem. What is going on with this article?

Node.jsとMeCabで品詞抽出してBotに返信させてみる。

More than 3 years have passed since last update.

Node.js 用モジュールmecab-asyncを利用することで手軽に形態素解析を行うことが出来ます。

解析結果から品詞の抽出をすることで、Botのオウム返しが自然になるのではないかと思い試してみました。

環境

  • Mac OSX10.11.5
  • Node.js v6.2.0
  • mecab 0.996
  • mecab-ipadic-neologd 2.0

チャットのフレームワーク

expresssocket.ioを利用してまずはシンプルに投稿をオウム返しさせてみます。

pondy 2016-07-17 14-15-16.png

package.json
{
  "dependencies": {
    "socket.io": "^1.4.6",
    "express": "^4.13.4"
  }

app.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
   socket.on('chat message', function(msg){

   var your_reply = 'あなた > ' + msg;
   io.emit('chat message', your_reply);
    var bot_reply = 'ポンディ > ' +  msg + 'だボット';
    io.emit('chat message', bot_reply);

});
});

http.listen(process.env.PORT || 3000, function(){
  console.log('listening on *:3000');
});

index.html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>pondy</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      #title { background: #fff; padding: 10px; position: fixed; top: 0; width: 100%; }
      form { background: #ddd; padding: 20px 10px 10px; position: fixed; bottom: 0; width: 100%; }
      #messages { list-style-type: none; margin-top: 6em;}
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
      #footer {margin-top:20px;}
    </style>
  </head>
  <body>
    <h1 id="title">pondy</h1>
    <ul id="messages"></ul>
    <form action="">

      <div class="form-group">
      <input id="m" autocomplete="off" type="text" class="form-control" placeholder="メッセージを入力">
      </div>
      <button type="submit" value="talk"  class="btn btn-default">送信</button>
      <br>
      <p id="footer" class="small">© 2016 PonDad All Right Reserved.</p>
    </form>

    <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
    <script src="http://code.jquery.com/jquery-1.11.1.js"></script>
    <script>
      var socket = io();
      $('form').submit(function(){
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
      });
      socket.on('chat message', function(msg){
        $('#messages').append($('<li>').text(msg));
      });
    </script>

  </body>
</html>

mecab-async で品詞を抽出する

MeCab辞書はmecab-ipadic-NEologdを使わせて頂きました。

「こんにちは、サミュエルLジャクソンです。」を形態素解析するとこの様な結果が得られます。

terminal
$ node app.js
[ { kanji: 'こんにちは',
    lexical: '感動詞',
    compound: '*',
    compound2: '*',
    compound3: '*',
    conjugation: '*',
    inflection: '*',
    original: 'こんにちは',
    reading: 'コンニチハ',
    pronunciation: 'コンニチワ' },
  { kanji: '、',
    lexical: '記号',
    compound: '読点',
    compound2: '*',
    compound3: '*',
    conjugation: '*',
    inflection: '*',
    original: '、',
    reading: '、',
    pronunciation: '、' },
  { kanji: 'サミュエルLジャクソン',
    lexical: '名詞',
    compound: '固有名詞',
    compound2: '一般',
    compound3: '*',
    conjugation: '*',
    inflection: '*',
    original: 'サミュエル・L・ジャクソン',
    reading: 'サミュエルエルジャクソン',
    pronunciation: 'サミュエルエルジャクソン' },
  { kanji: 'です',
    lexical: '助動詞',
    compound: '*',
    compound2: '*',
    compound3: '*',
    conjugation: '特殊・デス',
    inflection: '基本形',
    original: 'です',
    reading: 'デス',
    pronunciation: 'デス' },
  { kanji: '。',
    lexical: '記号',
    compound: '句点',
    compound2: '*',
    compound3: '*',
    conjugation: '*',
    inflection: '*',
    original: '。',
    reading: '。',
    pronunciation: '。' } ]

mecab-asyncを利用して感動詞と名詞を抽出してみます。

app.js
var MeCab = new require('mecab-async')
var mecab = new MeCab();
    MeCab.command = "mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd"
    var text = 'こんにちは、サミュエルLジャクソンです。'
    //注:パースコマンドを利用する時 "MeCab.~"と大文字にしないと動かないみたいです
    MeCab.parseFormat(text, function(err, morphs) {
        if (err) throw err;
        morphs.map(function(morph) {
        if (morph.lexical === '感動詞') {
          console.log(morph.lexical + ' : ' + morph.original);
        }
        if (morph.lexical === '名詞') {
          console.log(morph.lexical + ' : ' +morph.original);
        }
    });
    });

実行してみます

terminal
$ node app.js
感動詞 : こんにちは
名詞 : サミュエル・L・ジャクソン

今回は辞書を生成するのに適しているということで、mapクラスを利用してみました。これを使うとキーを利用して任意のデータを取得することが可能になります。

解析結果(ここではmorphs)の配列の中から、キーlexicalを指定する事で指定した品詞の形態素を抽出する事が出来ました。

さて、これを利用してチャット投稿を形態素解析してみる事にします。

品詞抽出してBotに返信させてみる

今回は「感動詞」と「固有名詞」、「自立形容詞」を抽出してBotのオウム返しに変化をつけてみたいと思います。

pondy 2016-07-17 14-11-14.png

app.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var MeCab = new require('mecab-async')
var mecab = new MeCab();
var array = [];
var reply = [];

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
   socket.on('chat message', function(msg){

   var your_reply = 'あなた > ' + msg;
   io.emit('chat message', your_reply);

   MeCab.command = "mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd"
    MeCab.parseFormat(msg, function(err, morphs) {
        if (err) throw err;
        morphs.map(function(morph) {
        if (morph.lexical === '感動詞') {
          array.push(morph.original);
          reply = '!'
          console.log(array);
        }
        if (morph.compound === '固有名詞') {
          array.push(morph.original);
          reply = 'っていいよね。'
          console.log(array);
        }
        if (morph.lexical === '形容詞' | morph.compound === '自立') {
          array.push(morph.original);
          console.log(array);
          reply = 'って楽しそうだね。'
        }
    });
    if(array.length <= 0){
      bot_reply = 'ポンディ > そうなんだ。' ;
    }else{
      bot_reply = 'ポンディ > ' +  array[Math.floor(Math.random() * array.length)] + reply;
    }
    io.emit('chat message', bot_reply);
    array = [];
    reply = [];
    });
});
});

http.listen(process.env.PORT || 3000, function(){
  console.log('listening on *:3000');
});

シンプルチャットのapp.jsを置き換えます。ターミナルで確認すると投稿から指定した品詞(ここでは「感動詞」・「固有名詞」・「自立形容詞」)を抽出しているのが分かります。

複数同じ品詞が含まれることもあるので、ここでは抽出した形態素をpushを利用して変数arrayに格納しています。

terminal
$ node app.js
listening on *:3000
[ 'こんにちは' ]
[ '妖怪ウォッチ' ]
[ '妖怪ウォッチ', 'アイカツ!' ]
[ 'ジバニャン' ]
[ 'かわいいよ' ]
[ '欲しい' ]
[ 'うん' ]
[ 'うん', 'さようなら' ]
[ 'さよなら' ]

今回はarrayに格納した配列をランダムで返信させてみました。

「感動詞」(例:こんにちは)を含む際は感動詞を返信。(例:「ポンディ こんにちは」→「こんにちは!」)

「固有名詞」(例:妖怪ウォッチ・アイカツ!)を含む際は固有名詞に「っていいよね。」をつけて返信。(例:「私ね 妖怪ウォッチとアイカツが好きなの」→「アイカツ!っていいよね。」)

「自立形容詞」(例:欲しい)を含む際は自立形容詞に「って楽しそうだね」をつけて返信。(例:「ぬいぐるみが欲しいの」→「欲しいって楽しそうだね。」)

単純なオウム返しと比較すると、少し自然な相槌になっている様な気がします。

まとめ

今回は子供がゆるキャラのぬいぐるみに話しかける様な場面を思いながら返信を書いてみました。簡単な雑談ならこれ位シンプルでも成り立つ様な気もします。(すぐ飽きちゃうだろうけど)

品詞抽出の条件や返答を工夫すればもう少し雑談Botらしくなるかもしれませんね。では。

参考にさせていただきました

Node.jsでmecab-ipadic-NEologdを使う - knjcode blog

Botと対話する - SlideShare

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away