negaposi-analyzer-jaをブラウザで使う


もちろんNode.js無しで。
普通は素直にビルドしましょうね。

依存するkuromoji.jsの導入は前回のkuromoji.jsをNode.jsなしでローカルブラウザで扱う - Qiitaのやりかたで。

negaposi--analyzer-ja とは

形態素解析したtokenからネガティブ/ポジティブを判定したスコアを返すJavaScriptライブラリ

欲しかったものそのものがまさに用意されていた安定のazu氏製です。

テキストから感情を評価するウェブサービスなどは時折話題になるが、それが「APIとして無料で手軽に」となると、とたんに見つからず、RubyやPythonとかじゃなくブラウザで実現したい要望をかなえてくれる可能性があるJavaScript製。

これをブラウザで動かすため、形態素解析用のkuromoji.jsともども無理やり書き換える。

ブラウザで読めるようにする

そのまま
negaposi-analyzer-ja.js
をscriptタグで読み込むと
Uncaught ReferenceError: require is not defined
なるほどrequireはブラウザのjsではできない。
幸いなことに
negaposi-analyzer-ja.js
で使われているrequire

const debug = require("debug")("negaposi-analyzer-ja");

    // 辞書の配列
    posiNegaDict: require("../dict/pn_ja.dic.json")

だけなので、無理やり書き換えることはできそうだ。

debugのほうは読み込みと実行のコメントアウトで対処。
辞書配列は別途グローバル変数に用意してあげることにする。
個人用なのでそんな無茶もOKなのだ。よって後述。

辞書を用意する

negaposi-analyzer-ja
で利用している辞書は残念ながら再配布不可で、jsではそのcsv辞書をjsonに変換して利用している。
ありがたいことにダウンロードとコンバートをこなすスクリプトが同梱されているのだが、もちろんnode.js環境がないので使えない。
かたくなにnode.jsを使わないへんな人はとりあえずオンラインに頼む。

適当にコンバートできるサイトを探して、
たとえば
CSV To JSON Converter
テキストをコピペで貼り付ける。
ここのデフォルトだと最初の一行目が名前/keyになるので、
サンプルのJSON
negaposi-analyzer-ja/example-dict.json at master · azu/negaposi-analyzer-ja · GitHub
を見ながら一行目に
surface:reading:pos:rank
を追加して変換してあげる。

今回は無理やりグローバル変数にするので、
pn_ja.dic.json.js
というファイルで、
先頭に
var dic =
を付けて

var dic = [
 {
   "surface": "優れる",
   "reading": "すぐれる",
   "pos": "動詞",
   "rank": 1
 },
 {

という形にするといいだろうか。

がしかし、
改めてサンプルを見ると読み方がカタカナになっていることに気づける。

エクセルがあれば変換は容易そうなのだが、あいにくOpenOfficeすらインストールしていない。

せいぜいGoogleのアカウントはあるだろうからスプレッドシートで代用できる。

SpreadsheetsでExcelのPHONETIC的なことがしたい! - メンチカツには醤油でしょ!!

やってることはjsなので自分でぱぱっと変換出力するHTMLを書いてもいい。

JavaScriptでカタカナをひらがなに変換する(その逆も) - Qiita

私は普段スプレッドシートを扱っておらず勉強から逃げるためにJSで書くことにする。
出力のjsonが非整形になるが、逆に圧縮できていいだろう。

HTMLのdivに書いてコピペするのは量が量なのでコンソールやスクラッチパッドではつらいかなーと思ったからです。

超雑なカタカナ化
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="./pn_ja.dic.json.js"></script>
</head>
<body>

<div></div>

</body>
<script type="text/javascript">
    function hiraToKana(str) {
        return str.replace(/[\u3041-\u3096]/g, function(match) {
            var chr = match.charCodeAt(0) + 0x60;
            return String.fromCharCode(chr);
        });
    }

    var newDic = dic.map((obj) => {
        obj.reading = hiraToKana(obj.reading)
        return obj
    })

    document.querySelector('div').textContent = JSON.stringify(newDic)

</script>
</html>

で、出力をやはりvar dic =を付けてファイルへ上書きしてあげます。

pn_ja.dic.json.js
var dic = [{"surface":"優れる","reading":"スグレル","pos":"動詞","rank":1},{"surface":"良い","reading":"ヨイ","pos":"形容詞","rank":0.999995},...

これで辞書はできたと思います。
もっと気になる人は
negaposi-analyzer-ja/dict-to-json.js at master · azu/negaposi-analyzer-ja · GitHub
を見ながらなんかを似せてください。

これで辞書配列ができたので、読み込み順に気をつけてスクリプトを書き換えましょう

negaposi-analyzer-ja.js
const defaultOptions = {
    // 辞書にない単語のスコア
    unknownWordRank: 0,
    // ポジティブな単語に対する補正値(スコアに乗算)
    positiveCorrections: 1,
    // ネガティブな単語に対する補正値(スコアに乗算)
    negativeCorrections: posiNegaRatio.posi / posiNegaRatio.nega,
    // 辞書の配列
    //posiNegaDict: require("../dict/pn_ja.dic.json")
    posiNegaDict: dic
};

Moduleを対応する

これでいけると思っていたのですが、読み込むと
Uncaught ReferenceError: module is not defined
あちゃーmodule.exportsもありませんでしたか。

/**
 * @param {Object[]} tokens kuromoji.jsのtoken配列
 * @param {Object} options
 */
module.exports = function(tokens, options = {}) {

じゃあとexport functionに書き換えてみたら、

Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.

とMIMEが正しくないと怒られる。
うーむ、localな処理なので如何したものか…

google chrome - ES6 modules in local files - The server responded with a non-JavaScript MIME type - Stack Overflow

かんがえるのがめんどうになったのでぐろーばるへんすうにもたせるよ
すでにいちどしているからいっしょいっしょ

var analyze = function(tokens, options = {}) {

あとはkuromojiのトークンを渡せばOK。

これは良い文章だと思います。という文字列を渡して
0.0899567537384933
が返ってきたのでREADMEと一緒。OK!
(ここで本家自体でCLIとjsとで値が変わっていることに気づいたがよくわからない)

完成動作テスト
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script src="./kuromoji.js"></script>
    <script src="./pn_ja.dic.json.js"></script>
    <script src="./negaposi-analyzer-ja.js"></script>
    <script>

kuromoji.builder({ dicPath: "dict/" }).build(function (err, tokenizer) {
    // tokenizer is ready
    var path = tokenizer.tokenize("すもももももももものうち");
    var path = tokenizer.tokenize("これは良い文章だと思います。");
    console.log(path);
    console.log(analyze(path));
});
    </script>
</head>
<body>
test
</body>
</html>

これanalyzerに渡したあとでdic変数をnullにしたらメモリ節約になるのかしらん?

さらっと試したところ、信者がネガティブかつ語彙がないので、
大人気配信者はネガティブになってしまったりするので面白い。

というか短文では結構ネガティブによりやすい気がします。
汚いなどのネガティブワードは納得できるのですが、
「え?その言葉がネガティブなの?」
というのが多いです。
解説にあるようにポジティブな単語の割合が少なかったり、
ワンセンテンスごとの解析では変動する言葉がひとつだったりするので、軽い副詞でネガティブになったりします。
さらに「好き」などが0評価で喜んでいるつもりがぼんやりしている人になったり。

そこは辞書に直接単語を追加したり、辞書にない単語のスコア

const defaultOptions = {
    // 辞書にない単語のスコア
    unknownWordRank: 0,

をポジティブに割り振ってあげると(スコア上の)元気が出てくると思いました。