Yahoo!!日本語形態素解析で文章中に存在する単語を解析します。そして使用されている回数もあわせて表示させ、解析結果をexcelファイルとしてダウンロードできるようにします。
サンプルページはこちらです。
まずExcelファイルをダウンロードする処理をするためにwwwroot/index.htmlにscriptを追加します。
wwwroot/index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Yahoo!!日本語形態素解析</title> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> </head> <body> <app>Loading...</app> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">??</a> </div> <script src="_framework/blazor.webassembly.js"></script> <!-- excelファイルのダウンロード関連 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.2/xlsx.full.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script> <script> function DownloadExcelFile(jsontext) { let write_opts = { type: 'binary' }; let sheetName = "Sheet1"; let sheets = {}; sheets[sheetName] = XLSX.utils.aoa_to_sheet(JSON.parse(jsontext)); let wb = { SheetNames: [sheetName], Sheets: sheets }; let wb_out = XLSX.write(wb, write_opts); let buf = new ArrayBuffer(wb_out.length); let view = new Uint8Array(buf); for (var i = 0; i != wb_out.length; ++i) view[i] = wb_out.charCodeAt(i) & 0xFF; let blob = new Blob([buf], { type: 'application/octet-stream' }); saveAs(blob, 'result.xlsx'); } </script> <!-- excelファイルのダウンロード関連 ここまで --> </body> </html> |
Yahoo!!日本語形態素解析の結果を取得するために同じドメインにphpをアップロードしておきます。
sample-data/abc.php
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php $sentence = $_POST["Sentence"]; $filter = $_POST["Filter"]; $appid = 'アプリケーションIDは各自で取得してください'; $url = "http://jlp.yahooapis.jp/MAService/V1/parse?appid=".$appid."&results=uniq"; $url .= "&sentence=".urlencode($sentence); $url .= "&filter=".urlencode($filter); $url .= "&response=".urlencode("surface,pos,baseform"); $homepage = file_get_contents($url); echo $homepage; ?> |
razorファイルに以下のコードを書きます。
文章を入力、取得したい品詞にチェックをいれて解析ボタンをおすと処理がおこなわれます。解析処理のあとダウンロードボタンをおすとExcelファイルがダウンロードされます。
Pages/Index.razor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
@page "/" <h1>Yahoo!!日本語形態素解析</h1> <p>Yahoo!!日本語形態素解析で文章の解析をおこないます。</p> <p>@str</p> @if (Words.Count != 0) { <table class="table"> <thead> <tr> <th>回数</th> @if(!BaseOnly){ <th>表記</th> } <th>基本形</th> <th>品詞</th> </tr> </thead> <tbody> @foreach (var word in Words) { <tr> <td>@word.Count</td> @if(!BaseOnly){ <td>@word.Surface</td> } <td>@word.Baseform</td> <td>@word.Pos</td> </tr> } </tbody> </table> } <textarea id="text1" rows="10" cols="60" @bind="@Sentence"></textarea> <br /> <input type="checkbox" @bind="@BaseOnly" />基本形のみを評価する <br /> <input type="checkbox" @bind="@Filter1" />1 : 形容詞 <input type="checkbox" @bind="@Filter2" />2 : 形容動詞 <input type="checkbox" @bind="@Filter3" />3 : 感動詞 <br /> <input type="checkbox" @bind="@Filter4" />4 : 副詞 <input type="checkbox" @bind="@Filter5" />5 : 連体詞 <input type="checkbox" @bind="@Filter6" />6 : 接続詞 <br /> <input type="checkbox" @bind="@Filter7" />7 : 接頭辞 <input type="checkbox" @bind="@Filter8" />8 : 接尾辞 <br /> <input type="checkbox" @bind="@Filter9" />9 : 名詞 <input type="checkbox" @bind="@Filter10" />10 : 動詞 <input type="checkbox" @bind="@Filter11" />11 : 助詞 <input type="checkbox" @bind="@Filter12" />12 : 助動詞 <br /> <input type="checkbox" @bind="@Filter13" />13 : 特殊(句読点、カッコ、記号など) <br /> <button @onclick="OnClickAnalysis">解析</button> <button @onclick="OnClickDownload">ダウンロード</button><br /> <button @onclick="OnClickAllCheck">全部チェック</button> <button @onclick="OnClickAllUnCheck">全部チェックをはずす</button> <!-- Begin Yahoo! JAPAN Web Services Attribution Snippet --> <a href="http://developer.yahoo.co.jp/about"> <img src="http://i.yimg.jp/images/yjdn/yjdn_attbtn2_105_17.gif" width="105" height="17" title="Webサービス by Yahoo! JAPAN" alt="Webサービス by Yahoo! JAPAN" border="0" style="margin:15px 15px 15px 15px"> </a> <!-- End Yahoo! JAPAN Web Services Attribution Snippet --> |
Pages/Index.razorのフィールド変数部分を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@code { @using System.Text.Json; @inject IJSRuntime JS; @inject HttpClient Http public string Sentence = "解析したい文章を入力してください。"; public string str = ""; // チェックボックスはチェックされているか? public bool Filter1 = true; // 形容詞 public bool Filter2 = true; // 形容動詞 public bool Filter3 = true; // 感動詞 public bool Filter4 = true; // 副詞 public bool Filter5 = true; // 連体詞 public bool Filter6 = true; // 接続詞 public bool Filter7 = true; // 接頭辞 public bool Filter8 = true; // 接尾辞 public bool Filter9 = true; // 名詞 public bool Filter10 = true; // 動詞 public bool Filter11 = true; // 助詞 public bool Filter12 = true; // 助動詞 public bool Filter13 = true; // 特殊(句読点、カッコ、記号など) public bool BaseOnly = false; // 基本形のみを評価する } |
Wordsには解析結果がはいります。
1 2 3 4 5 6 7 8 9 10 11 |
@code { private List<Word> Words = new List<Word>(); public class Word { public string Surface = ""; // 表記 public string Pos = ""; // 品詞名 public string Baseform = ""; // 基本形 public string Count = ""; // 回数 } } |
全部チェックする、全部チェックをはずすをクリックしたらOnClickAllCheck()メソッドとOnClickAllUnCheck()メソッドが呼び出されるのでチェックボックスの状態を変化させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@code { void OnClickAllCheck() { Filter1 = true; Filter2 = true; Filter3 = true; Filter4 = true; Filter5 = true; Filter6 = true; Filter7 = true; Filter8 = true; Filter9 = true; Filter10 = true; Filter11 = true; Filter12 = true; Filter13 = true; } void OnClickAllUnCheck() { Filter1 = false; Filter2 = false; Filter3 = false; Filter4 = false; Filter5 = false; Filter6 = false; Filter7 = false; Filter8 = false; Filter9 = false; Filter10 = false; Filter11 = false; Filter12 = false; Filter13 = false; } } |
解析をクリックしたときに呼び出されるOnClickAnalysis()メソッドを示します。
POST 要求を非同期操作として送信し、結果がXmlで返されるのでこれをGetResultFromXmlメソッドでWordのリストに変換して取得します。
リクエストパラメータとしてappid(アプリケーションID)、sentence(解析対象のテキスト)、results(解析結果の種類)、filter(解析結果として出力する品詞番号を “|” で区切って指定)が必要ですが、appidとresultsは前述のPOST 要求をするphpファイルに直接書いています。sentenceはSentenceに格納されているものを、filterはGetFilterString()メソッドでチェックボックスから取得します。
解析すべき文章が存在しない場合やどの品詞名にもチェックがされていない場合はエラーメッセージを表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
@code { private async Task OnClickAnalysis() { string filter = GetFilterString(); if (Sentence == "") { str = "エラー:解析する文章を入力してください。"; return; } if (Sentence.Length > 10000) { str = "エラー:解析できる文章は10000字までです。"; return; } if (filter == "") { str = "エラー:取得する品詞にチェックをつけてください。"; return; } var parameters = new Dictionary<string, string>() { { "Sentence", Sentence }, { "Filter", filter }, }; var content = new FormUrlEncodedContent(parameters); var res = await Http.PostAsync("sample-data/abc.php", content); Task<string> task = res.Content.ReadAsStringAsync(); string ret = task.Result; GetResultFromXml(ret); str = ""; } string GetFilterString() { List<string> filters = new List<string>(); if (Filter1) filters.Add("1"); if (Filter2) filters.Add("2"); if (Filter3) filters.Add("3"); if (Filter4) filters.Add("4"); if (Filter5) filters.Add("5"); if (Filter6) filters.Add("6"); if (Filter7) filters.Add("7"); if (Filter8) filters.Add("8"); if (Filter9) filters.Add("9"); if (Filter10) filters.Add("10"); if (Filter11) filters.Add("11"); if (Filter12) filters.Add("12"); if (Filter13) filters.Add("13"); if (filters.Count != 0) return String.Join("|", filters.ToArray()); else return ""; } } |
Postすると解析結果が以下のような形で返されるので、これをGetResultFromXml(string xml)メソッドでWordのリストに変換します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?xml version="1.0" encoding="UTF-8" ?> <ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:jp:jlp" xsi:schemaLocation="urn:yahoo:jp:jlp https://jlp.yahooapis.jp/MAService/V1/parseResponse.xsd"> <uniq_result> <total_count>9</total_count> <filtered_count>5</filtered_count> <word_list> <word> <surface>庭</surface> <reading>にわ</reading> <pos>名詞</pos> <baseform>庭</baseform> <count>1</count> </word> <word> <surface>二</surface> <reading>2</reading> <pos>名詞</pos> <baseform>2</baseform> <count>1</count> </word> <word> <surface>羽</surface> <reading>わ</reading> <pos>名詞</pos> <baseform>羽</baseform> <count>1</count> </word> <word> <surface>ニワトリ</surface> <reading>にわとり</reading> <pos>名詞</pos> <baseform>ニワトリ</baseform> <count>1</count> </word> <word> <surface>いる</surface> <reading>いる</reading> <pos>動詞</pos> <baseform>いる</baseform> <count>1</count> </word> </word_list> </uniq_result> </ResultSet> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@code { void GetResultFromXml(string xml) { System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.PreserveWhitespace = true; doc.LoadXml(xml); var nodes = doc.SelectNodes("//*[local-name()='word']"); Words = new List<Word>(); foreach (System.Xml.XmlNode node in nodes) { System.Xml.XmlNode node1 = node.SelectSingleNode("*[local-name()='surface']"); System.Xml.XmlNode node2 = node.SelectSingleNode("*[local-name()='pos']"); System.Xml.XmlNode node3 = node.SelectSingleNode("*[local-name()='baseform']"); System.Xml.XmlNode node4 = node.SelectSingleNode("*[local-name()='count']"); Word word = new Word(); if (node1 != null) word.Surface = node1.InnerText; if (node2 != null) word.Pos = node2.InnerText; if (node3 != null) word.Baseform = node3.InnerText; if (node4 != null) word.Count = node4.InnerText; Words.Add(word); if(BaseOnly) { Words = GetBaseOnlywords(Words).OrderByDescending(x => x.Count).ToList(); } } } } |
もし[基本形のみを評価する]がチェックされている場合は、GetBaseOnlywordsメソッドを呼び出してBaseformが同じものはまとめてしまってCountが大きい順にソートします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@code { public List<Word> GetBaseOnlywords(List<Word> words) { List<Word> newWords = new List<Word>(); var query = words.GroupBy(x => x.Baseform); foreach (var group in query) { Word newWord = new Word(); newWord.Baseform = group.Key; int allCount = 0; foreach (var item in group) { newWord.Pos = item.Pos; int count = int.Parse(item.Count); allCount += count; } newWord.Count = allCount.ToString(); newWords.Add(newWord); } return newWords; } } |
ダウンロードボタンがクリックされたらWordsに格納されている文字列をJson形式に変換してJavaScriptのDownloadExcelFile関数に渡します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@code { async void OnClickDownload() { if (Words.Count == 0) return; List<string[]> vs1 = new List<string[]>(); if(BaseOnly) vs1.Add(new string[]{"回数", "基本形", "品詞", }); else vs1.Add(new string[]{"回数", "表記", "基本形", "品詞", }); foreach (Word word in Words) { List<string> vs2 = new List<string>(); vs2.Add(word.Count); if(!BaseOnly) vs2.Add(word.Surface); vs2.Add(word.Baseform); vs2.Add(word.Pos); vs1.Add(vs2.ToArray()); } var jsonstr = JsonSerializer.Serialize(vs1.ToArray()); await JS.InvokeVoidAsync("DownloadExcelFile", jsonstr); } } |