この記事はNTTコミュニケーションズ Advent Calendar 2018 の15日目です。
はじめに
こんにちは、ozoraminatoと申します。
皆さんは「なろう小説」をご存知でしょうか?
「なろう小説」というのは、「小説家になろう」(以下「なろう」)というオンラインの小説投稿サイトに投稿されている小説の通称で、「Re:ゼロから始める異世界生活」「ダンジョンに出会いを求めるのは間違っているだろうか」といったアニメ化作品も含んで、その小説の投稿件数は2018年12月現在で60万件以上。
誰でも投稿できる小説投稿サイトなだけあって、その投稿・閲覧は全て無料。もちろん作品の質や傾向はバラバラですが、中にはつい時間を忘れて読みふけってしまうような作品もたくさんあります。
この「なろう」ですが、特徴の一つに「APIを提供している」ということが挙げられます。APIを利用することで、機械的に小説情報の検索・表示が可能になります。
オンライン小説とはいえ、60万件の小説作品を中身も含めてAPIで取得できるというのは驚異的ですし、データ分析の観点からも面白い素材といえるのではないでしょうか?
今回は、その「なろう」のAPIと、NTTコミュニケーションズが提供するCOTOHA APIを利用して、お手軽な自然言語処理をやってみたいと思います。
まずは「人気作品」をトップ100位まで取得。
なろう小説のAPI仕様書(リンク)を見て、おもむろにターミナルに以下のとおり入力します。
curl -i -X GET 'https://api.syosetu.com/novelapi/api/?out=yaml&lim=100' | less
するとこのように結果が表示されます。もちろん結果は取得タイミングによって変わります。
-
title: 大野のボクシング体験記
ncode: N4409FE
userid: 1273937
writer: 陶山雅司
story: 大野のボクシング体験
biggenre: 3
genre: 306
gensaku:
keyword: >
R15 残酷な描写あり 日常 青春
ハードボイルド ホームドラマ
ncode: N4409FE
userid: 1273937
writer: 陶山雅司
story: 大野のボクシング体験
biggenre: 3
genre: 306
gensaku:
keyword: >
R15 残酷な描写あり 日常 青春
ハードボイルド ホームドラマ
シリアス ダーク
general_firstup: 2018-12-12 13:45:17
general_lastup: 2018-12-12 14:17:00
novel_type: 1
end: 1
general_all_no: 3
length: 1036
time: 3
isstop: 0
isr15: 1
isbl: 0
isgl: 0
iszankoku: 1
istensei: 0
istenni: 0
pc_or_k: 2
global_point: 0
fav_novel_cnt: 0
review_cnt: 0
all_point: 0
all_hyoka_cnt: 0
sasie_cnt: 0
kaiwaritu: 97
novelupdated_at: 2018-12-12 14:17:00
updated_at: 2018-12-12 14:20:07
様々なパラメータがあることがわかります。では、ここからフィルタリング条件を追加して人気作品の取得を行ってみましょう。
「なろう」の人気作品は、pt数をベースにした「総合評価」を降順ソートすることで取得可能です。
curl -i -X GET 'https://api.syosetu.com/novelapi/api/?out=yaml&order=hyoka&lim=100' | less
-----
Date: Wed, 12 Dec 2018 05:30:25 GMT
Server: Apache
Content-Length: 176375
Content-Disposition: inline; filename=f717a3f1c0dd1b56f5cec7380272a529.yml
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain; charset=UTF-8
---
-
allcount: 615293
-
title: '無職転生 - 異世界行ったら本気だす -'
ncode: N9669BK
userid: 288399
writer: 理不尽な孫の手
story: |
34歳職歴無し住所不定無職童貞のニートは、ある日家を追い出され、人生を後悔している間にトラックに轢かれて死んでしまう。目覚めた時、彼は赤ん坊になっていた。どうやら異
世界に転生したらしい。
彼は誓う、今度こそ本気だして後悔しない人生を送ると。
【2015年4月3日23:00 完結しました】
完結後の番外編はこちらで連載中です。
無職転生 - 蛇足編 -
http://ncode.syosetu.com/n4251cr/
biggenre: 2
genre: 201
gensaku:
keyword: >
R15 残酷な描写あり
異世界転生
general_firstup: 2012-11-22 17:00:34
http://ncode.syosetu.com/n4251cr/
biggenre: 2
genre: 201
gensaku:
keyword: >
R15 残酷な描写あり
異世界転生
general_firstup: 2012-11-22 17:00:34
general_lastup: 2015-04-03 23:00:00
novel_type: 1
end: 0
general_all_no: 286
length: 2835125
time: 5671
isstop: 0
isr15: 1
isbl: 0
isgl: 0
iszankoku: 1
istensei: 1
istenni: 0
pc_or_k: 2
global_point: 466899
fav_novel_cnt: 149228
review_cnt: 140
all_point: 168443
all_hyoka_cnt: 17541
sasie_cnt: 4
kaiwaritu: 22
novelupdated_at: 2018-12-09 19:54:23
updated_at: 2018-12-12 14:22:23
1位の「無職転生」が出てきました。総合評価ptは466899点と最も高いことがわかります。ちなみにこの作品だと僕はアイシャが好きです。2位は「転生したらスライムだった件」でした。主人公が人の形になる過程が思ったより重たかった。3位は「ありふれた職業で世界最強」です。ユエ様最強。
では早速、この「人気作品」のデータを利用して少し遊んでみましょう。
最も多く使われている単語を探す
読者の皆様には、「『なろう』のタイトルの傾向を知りたい」と思う方も一定数いると思います。
というわけでここでは、「なろう」の人気上位100件のタイトルで最も多く使われている単語を探してみましょう。
なろう小説APIを叩いて人気上位100件の小説を取得する
まずはなろう上位100件のタイトルを取得します。
curl -S -X GET 'https://api.syosetu.com/novelapi/api/?out=json&order=hyoka&lim=100' | jq -r '.[] | .title'
----
無職転生 - 異世界行ったら本気だす -
転生したらスライムだった件
ありふれた職業で世界最強
デスマーチからはじまる異世界狂想曲( web版 )
とんでもスキルで異世界放浪メシ
Re:ゼロから始める異世界生活
謙虚、堅実をモットーに生きております!
...
このタイトルリストを、APIを用いて形態素解析します。
「形態素解析」とは、日本語文章を機械的に分かち書きすることです。例えば、「今日はいい天気ですね」という文章があったら、「今日/は/いい/天気/です/ね」といった具合に品詞を分解してくれます。
COTOHA APIを用いて形態素解析する
形態素解析は、NTTコミュニケーションズが提供しているCOTOHA APIを利用してみましょう。登録は無料です。
COTOHA APIには、形態素解析を始めとする様々な自然言語処理APIを提供しています。例えば、**「今日はいい天気ですね」**を構文解析にかけてみましょう。
% curl -H "Content-Type:application/json" -H "charset:UTF-8" -H "Authorization:Bearer XXXX" -X POST -d '{"sentence":"今日 はいい天気ですね"}' https://api.ce-cotoha.com/api/dev/nlp/v1/parse
{
"result" : [ {
"chunk_info" : {
"id" : 0,
"head" : 2,
"dep" : "D",
"chunk_head" : 0,
"chunk_func" : 1,
"links" : [ ]
},
"tokens" : [ {
"id" : 0,
"form" : "今日",
"kana" : "キョウ",
"lemma" : "今日",
"pos" : "名詞",
"features" : [ "日時" ],
"dependency_labels" : [ {
"token_id" : 1,
"label" : "case"
} ],
"attributes" : { }
}, {
"id" : 1,
"form" : "は",
"kana" : "ハ",
"lemma" : "は",
"pos" : "連用助詞",
"features" : [ ],
"attributes" : { }
} ]
}, {
"chunk_info" : {
"id" : 1,
"head" : 2,
"dep" : "D",
"chunk_head" : 0,
"chunk_func" : 1,
"links" : [ ]
},
...
上記のように結果を返してくれます。body部分は {"sentence":"<本文>"}
を指定すればよいので、その形になるよう、先程取得したタイトルの文字列をJSONファイルに加工しておきます。ここでは改行を半角スペースに置き換えて、以下のようにしてみました。
cat narou100.json
{"sentence": "無職転生 - 異世界行ったら本気だす - 転生したらスライムだった件 ありふれた職業で世界最強...<中略>...サモナーさんが行く 薬屋のひとりごと精霊幻想記(Web版) 異世界居酒屋「のぶ」"}
改めて以下のようにリクエストしてみます。なお、この際にjsonファイルの内容が大きすぎるとCOTOHA APIがエラーを返すので、文字列の大きさはほどほどにしておきましょう。COTOHA APIの出力結果は、cotoha_response.txt
に保存します。
curl -H "Content-Type:application/json" -H "charset:UTF-8" -H "Authorization:Bearer XXXX" -X POST -d @narou100.json https://api.ce-cotoha.com/api/dev/nlp/v1/parse > cotoha_response.txt
レスポンスには先のサンプルで得た様々な情報が記載されていますが、今回使いたいのは名詞の単語だけなので、jqコマンドで「単語-品詞」の形になるよう整形し、その中で「名詞」が含まれる単語を抽出、その数を降順にソートします。
そして得られた、**「なろう小説の人気作品で多く使われている単語」**はこちら!
% cat cotoha_response.txt| jq '.result[].tokens[] | [.form,"-", .pos] | add' | grep "名詞" | sort | uniq -c | sort -n -r
29 "世界-名詞"
21 "異-冠名詞"
15 "転生-名詞"
10 "最強-名詞"
8 "魔王-名詞"
8 "勇者-名詞"
6 "~-名詞接尾辞"
5 "賢者-名詞"
4 "スキル-名詞"
4 "様-名詞接尾辞"
4 "俺-名詞"
3 "ハーレム-名詞"
3 "チート-名詞"
3 "ため-補助名詞"
3 "魔法-名詞"
3 "者-名詞接尾辞"
3 "版-名詞接尾辞"
3 "士-名詞接尾辞"
3 "web-名詞"
……まあ、想像通りの結果でしたね。「異世界」を2単語として認識しているため2つに分かれていますが、**実質的なトップキーワードは「異世界」**といって良いでしょう。
COTOHAのキーワード抽出APIにかけてみる
では今度は、COTOHA APIの「キーワード抽出API」にかけてみましょう。キーワード抽出APIとは、
キーワード抽出APIは、入力として日本語で記述された複数の文からなるテキストを受け取り、テキストに含まれる特徴的なフレーズ・単語をキーワードとして抽出します。
とのこと。先程利用したnarou100.txtの中身を、"sentence":"..."
から"document":"..."
になるよう置き換えて、 narou100-keyword.txt
として保存します。
その状態でAPIをリクエストした結果がこちら。
% curl -H "Content-Type:application/json" -H "charset:UTF-8" -H "Authorization:Bearer XXXX" -X POST -d @narou100-keyword.txt https://api.ce-cotoha.com/api/dev/nlp/v1/keyword
{
"result" : [ {
"form" : "転生",
"score" : 150.6064
}, {
"form" : "異世界",
"score" : 81.2004
}, {
"form" : "勇者",
"score" : 31.2222
}, {
"form" : "俺",
"score" : 28.976849
}, {
"form" : "蜘蛛",
"score" : 26.9968
}, {
"form" : "ライフ",
"score" : 26.9968
}, {
"form" : "ダンジョンマスター",
"score" : 26.591301
}, {
"form" : "結構",
"score" : 25.6106
}, {
"form" : "無職",
"score" : 25.1642
}, {
"form" : "スライム",
"score" : 24.7996
} ],
"status" : 0,
"message" : ""
}%
……まあだいたい一緒ですね。
このように、「なろう」人気作品上位100個のタイトルの集合から得られたキーワードのうち特徴的なものは**「転生」「異世界」「勇者」**といった単語であることがわかりました。
これは単語カウントの傾向とも似ているので、キーワード抽出APIは文章の特徴をかなり上手くつかんでいることがわかります。また、先程は「異」と「世界」で分かれていた言葉が「異世界」になっていたり、**「ダンジョンマスター」**という言葉が入っているなど、一定の固有表現を考慮した結果となっていることもわかります。
形態素解析をベースにした解析をゴリゴリ行う場合、例えば上位にあった6 "~-名詞接尾辞"
の「~」や、恐らくタイトルの「(Web版)」という括弧書きから由来すると思われる3 "web-名詞"
の「web」など、ノイズと思われる結果の処理に苦戦することがままあります。そういった苦悩を少しでも楽にしてくれるAPIが提供されているというのはありがたい話ですね。
まとめ
いかがでしたでしょうか? ここでは、Web小説投稿サイト「小説家になろう」のAPIと、NTTコミュニケーションズが提供する「COTOHA」APIの2つを利用したお手軽な自然言語処理について解説させていただきました。
「COTOHA」のAPIにはここで紹介した以外にも様々な機能があります。
たとえばCOTOHAには「ユーザ属性推定」APIがあり、文章の書き手の、年代、性別、趣味、職業などの人物に関する属性を推定・出力するものもあります。主人公が転生サラリーマンの小説と主人公が乙女ゲームの悪役令嬢の小説をそれぞれ解析にかけてみたら、当然結果は違ってくることが推察されます。
それ以外でも、例えば本文を解析して先の**「キーワード抽出」**を行ったものをデータベース化し、そのキーワードが似通っているものは類似した作品として処理し、レコメンドエンジンとして実装するといった応用例も思いつきます。
自然言語処理といえば、昔は様々なライブラリをローカルマシンにインストールして、スクリプトをゴリゴリ書いてfor文で回すような世界でしたが、今はAPIでさっくり遊べる便利な時代になりました。
「なろう」以外にもいろいろなAPIがありますので、ぜひいろいろなサービスのAPIx自然言語処理で遊んでみてくださいね。