見出し画像

Webスクレイピング Part 5:「関数」ってこういうものだったんだ! ~『英単語1000本ノック』Ver 1を今度こそ完成させる!

さて、今回は英単語訓練アプリのバージョン1.0を完成させますが、その前に、これからコードが複雑になる前にいったん「整理整頓」をしておきます。プログラミングを始めたばかりの方は「整理って何するの?」と思いますが、ここでは「関数」という概念を理解するとそれがよくわかります。まずはこの点についてじっくり解説していきます。

値の入れ物が「変数」、処理の入れ物が「関数」

変数とは値の入れ物だという説明は『今すぐ書ける 1分間プログラミング』でも詳しく解説しています。

「入れ物」に何かを入れるというのはそこに入っている値を「何度も使いまわす」というのが目的です。同様に、一定の「処理」を何度も呼び出すこともプログラミングではよくやります。例えばこれまで作ってきた英単語訓練アプリも、次のような「一定の処理」というものがあります。

✅ TOEIC単語が掲載されたサイトから単語リストをスクレイプする
✅ 単語をもとに例文サイトから例文をスクレイプする

Part4までの段階で、まずは単語リストをスクレイプし、そこからランダムに単語を一つ選び、それをもとに例文をスクレイプするという流れになっています。例えば単語リストのサイトがいくつかある場合、その「スクレイプ処理」は、最初はサイトA、次はサイトBなどと何度も呼び出すこともできます。例文スクレイプは単語を選択するごとに実行するので、繰り返し同じ処理をしないといけません。このように「何度も呼び出す処理」は関数という入れ物に入れておくと非常に便利なのです

単語リストを取得する部分をGetWordListという「関数」にする

Part4の最後に書いたコードをもう一度見てください。Mainブロックの中で黄色い四角で囲んだ部分がTOEICの英単語サイトから単語リスト1000を取得する部分でした。

画像1

最後はスクレイプして取り出した英単語を一つ一つ、wordsというList変数に入れていきました。最後のforeeachループが終わると単語リストが出来上がっているという仕組みになっています。

そこで、この単語スクレイピングの処理をたったの1行、GetWordListという関数にしてしまうのが今回の作業です。具体的にはこんな1行になります。

List<string> words = GetWordList();

えっ?この黄色い部分が全部この1行に置き換わるの?」と驚くかもしれませんがそうなんです。GetWordListは文字通り「単語リストを取得する」ということ。こうした操作を「メソッド」と呼びます。

こんな簡単なコードで単語リストができるなら苦労はないのですが、もちろんその魔法のようなメソッドもその中身をどこかで書かないといけません。それを入れておくのが「関数」なんです。

手順としてはこうなります。

❶ Mainブロックの中のforeachでwordに単語を挿入している部分までをカットする
❷ Mainブロックの外、そのすぐ下に新たなブロックを作り、その名前として次のメソッド名を追加する: private static List<string> GetWordList()
❸ さっきカットした部分をこのGetWordListメソッドのブロック内に貼り付ける。

するとこんな感じになります(注:このコードはそのままではエラーになります)。

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace WebScrapingPart2
{
 class Program
 {
     static void Main(string[] args)
     {
         //================================
         //単語リストからランダムに一つ選ぶ
         //================================
         Random r = new Random();
         string word = words[r.Next(0, words.Count - 1)];
         Console.WriteLine("次の単語の例文を表示します:" + word);
           //================================
          //選んだ単語の例文を表示させる
          //================================
          //Weblioサイトから例文を抽出する
          //週出した例文を一つ一つ表示させる

         Console.ReadLine();
     }

     private static List<string> GetWordList () 
     {
         //================================
         //Webサイトを開いてHTMLを読む。
         //================================
         //英単語サイトのURLを変数urlに入れる
         string url = @"https://toiguru.jp/toeic-vocabulary-list";
         //HTMLを読み込むWebClientを作る
         var wc = new WebClient();
         //WebClientのDownloadStringメソッドでHTMLを読み込む
         string html = wc.DownloadString(url);
         //================================
         //単語のパターンを抽出する
         //================================
         //単語リストの変数を作る
         var words = new List<string>();
         //<td>なんちゃら<br>なんちゃら</td>に合致したテキストを取り出す
         var matches = Regex.Matches(html, @"<td>(.+?)<br>(.+?)</td>");
         //合致したものを単語リストに入れていく
         foreach (Match match in matches)
         {
             //英語を単語リストに加える
             words.Add(match.Groups[1].Value);
         }
     }
 }
}

このGetWordListのブロックが「関数」になります。つまり、この切り離した部分でTOEICサイトから単語をスクレイプする処理を実行するということです。そして関数では最後に単語リストを”返して”あげる必要があります。どうするかというと、最後にwordsリストをreturn(返す)する行を加えてあげるだけです。

     private static List<string> GetWordList () 
     {
         //================================
         //Webサイトを開いてHTMLを読む。
         //================================
         //英単語サイトのURLを変数urlに入れる
         string url = @"https://toiguru.jp/toeic-vocabulary-list";
         //HTMLを読み込むWebClientを作る
         var wc = new WebClient();
         //WebClientのDownloadStringメソッドでHTMLを読み込む
         string html = wc.DownloadString(url);
         //================================
         //単語のパターンを抽出する
         //================================
         //単語リストの変数を作る
         var words = new List<string>();
         //<td>なんちゃら<br>なんちゃら</td>に合致したテキストを取り出す
         var matches = Regex.Matches(html, @"<td>(.+?)<br>(.+?)</td>");
         //合致したものを単語リストに入れていく
         foreach (Match match in matches)
         {
             //英語を単語リストに加える
             words.Add(match.Groups[1].Value);
         };

          //単語が入ったwordリストを「返す」
          return words;

     }

この関数の目的はGetWordListです。つまり単語リストをGett(取得)するということ。なのでこの関数を呼び出した結果として単語リストがポンと渡されることにならないといけません。この「ポン」をするのがreturnというコマンドです。return words; は文字通り「wordsリストを返す」ということですよね。

とりあえず単語スクレイピング部分をGetWordListという新たなメソッドのブロックに移動しただけです。「なんのためにこんなことをするの」と思うかもしれませんが、後で十分納得できるので、とりあえず今はそのままついてきてください。

関数を「呼び出す」ということ

さて、TOEIC単語のスクレイピング部分をごっそりGetWordListに移動させました。これはMainブロックの外にあるので、このままだと何も起こりません。肝心なのは「関数」という入れ物に入れた単語リスト抽出メソッドを「呼び出す」必要があります。

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace WebScrapingPart2
{
 class Program
 {
     static void Main(string[] args)
     {
         //単語リスト取得メソッドを呼び出す
         List<string> words = GetWordList();
        
         //================================
         //単語リストからランダムに一つ選ぶ
         //================================
         Random r = new Random();
         string word = words[r.Next(0, words.Count - 1)];
         Console.WriteLine("次の単語の例文を表示します:" + word);
           //================================
          //選んだ単語の例文を表示させる
          //================================
          //Weblioサイトから例文を抽出する
          //週出した例文を一つ一つ表示させる

         Console.ReadLine();
     }

     private static List<string> GetWordList () 
     {
         //================================
         //Webサイトを開いてHTMLを読む。
         //================================
         //英単語サイトのURLを変数urlに入れる
         string url = @"https://toiguru.jp/toeic-vocabulary-list";
         //HTMLを読み込むWebClientを作る
         var wc = new WebClient();
         //WebClientのDownloadStringメソッドでHTMLを読み込む
         string html = wc.DownloadString(url);
         //================================
         //単語のパターンを抽出する
         //================================
         //単語リストの変数を作る
         var words = new List<string>();
         //<td>なんちゃら<br>なんちゃら</td>に合致したテキストを取り出す
         var matches = Regex.Matches(html, @"<td>(.+?)<br>(.+?)</td>");
         //合致したものを単語リストに入れていく
         foreach (Match match in matches)
         {
             //英語を単語リストに加える
             words.Add(match.Groups[1].Value);
         };

          //単語が入ったwordリストを「返す」
          return words;
     }
 }
}

そうです、Mainメソッドの中の冒頭に次の「関数呼び出し行」を入れるだけです。

 //単語リスト取得メソッドを呼び出す
 List<string> words = GetWordList();
        

これで文字列のリスト、wordsに単語帳が入ります。これでアプリを起動させるとPart4でやったのと全く同じく、コンソール画面にランダムで選択された単語が一つ表示されるはずです。

「関数」にすることで処理を簡単に変更できるようになる!

以前と全く同じことをするということは、関数にしたところで何も効果はないのでしょうか?単語スクレイピングのコードだけをちょっと下の方にまとめるだけで、それが一体何の意味を持つのでしょう。例えば、こんなことをしたいと思ったらどうでしょう。

今は特定のサイトのTOEIC単語をスクレイプして単語帳を作ったけど、TOEIC単語サイトはほかにもあるかもしれない。さらに他の英語のテストの単語も練習したい。例えばTOEFLとかアメリカの大学入試に使われているSATやACTなどの単語も学習できるようにしたい。

まさにこれが「機能の拡張」です。そこでこの関数にパラメータを設定して、単語リストを作るテストの種類を変更できるようにしたらどうでしょう。

private static List<string> GetWordList (string TestType) 

これを呼び出すには次のようにテストのタイプとして"TOEIC"のような文字列を渡すのです。

 //単語リスト取得メソッドを呼び出す
 List<string> words = GetWordList("TOEIC");

こうすると、TOEIC単語を勉強したい時は"TOEIC"、SAT単語をリストするには”SAT"という文字列を渡すだけで自由に様々な単語リストを取得できるようになります。処理を関数という入れ物に入れる利点の一つにはこの”拡張性”を持たせることにあるのです。ただ単にコードを整理しただけではないのです。

画像3

関数を使ってアプリを完成させる!

ではまず、例文のスクレイピングのところも関数にしたMainブロックをみてみます。

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace WebScrapingPart2
{
   class ProgramPart2
   {
       static void Main(string[] args)
       {
           //================================
           // Step 1:GetWordListメソッドで単語リストを取得
           //================================
           var words = GetWordList();

           //================================
           //Step 2:単語リストからランダムに一つ選ぶ
           //================================
           Random r = new Random();
           string word = words[r.Next(0, words.Count - 1)];

           Console.WriteLine("次の単語の例文を表示します:" + word);
           Console.WriteLine(); //一行空ける

           //=================================================
           //Step 3: 選んだ単語の例文をGetSampleSentencesメソッドで表示させる
           //===============================================

           //スクレイプ関数、GetSampleSentencesを呼ぶ
           var samples = GetSampleSentences(word);

           //例文リストの中身を一つずつ表示する
           foreach (var sample in samples)
           {
               Console.WriteLine(sample.Key); //英語の表示
               Console.WriteLine(sample.Value); //日本語の表示

               //一行空ける
               Console.WriteLine();
           }

           Console.ReadLine();
       }

Mainメソッドがこんなにすっきりしていますが、それは単語と例文のスクレイピングコードを関数の入れ物に入れたからです。関数はMainメソッドの外に書くのでここには含まれません。すっきりしているのはそのためです。

ここではわかりやすく3つのステップに分けてコードを書いています。

ステップ1:単語リストを取得する

これは関数のおかげでたったの1行で済んでいます。GetWordListを呼び出すと単語リスト(データ型はList<string>)、wordsに結果をぶちこんでくれます。

 //================================
 // Step 1:GetWordListメソッドで単語リストを取得
 //================================
 var words = GetWordList();

単語リスト取得の関数、GetWordListの説明はすでにしていますが、改めて完成形の関数をみてみます。

       /// <summary>
       /// 英単語リストをスクレイピングで取得する。TOEIC英単語のみサポート
       /// </summary>
       /// <returns></returns>
       private static List<string> GetWordList ()
       {
           //単語リストの変数を作る
           var words = new List<string>();

           //================================
           //Webサイトを開いてHTMLを読む。
           //================================
           //WordHippoのURLを変数urlに入れる
           string url = @"https://toiguru.jp/toeic-vocabulary-list";
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

           //================================
           //単語のパターンを抽出する
           //================================
           //<td>なんちゃら<br>なんちゃら</td>に合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<td>(.+?)<br>(.+?)</td>");
           //合致したものをすべて表示する
           foreach (Match match in matches)
           {
               //英語を単語リストに加える
               words.Add(match.Groups[1].Value);
           }
           return words;
       }

ここのコードはすべてPart3で解説しています。ただ、関数の入れ物に入れただけです。関数に入れると次の2点を変更します。まず関数の大枠はこんな感じです。

private static List<string> GetWordList ()
{
    //
    //関数の中身のコード(ここは前と同じ)
    //

    return words;
}

【関数の宣言】private static List<string> GetWowrdList
✅ 冒頭のprivate staticは説明が複雑になるのでここではおまじないのようなものだと考えてください。後のPartで詳しく解説します。
List<string>というのはこの関数から出てくるデータのタイプ(型)です。ここでは単語リストをGetする関数なので、そのデータ型は文字列のListです。
✅ 関数名、GetWordListの後の括弧には引数を入れることができますが、ここではなにも使用しません。前に説明したように、この関数を、TOEICだけではなくTOEFLやSATなど他のテストの単語リストも作れるようにするには、ここに引数を入れて、”TOEIC”や”SAT"などを渡すようにできます。今はとりあえずデフォルトでTOEICの単語リストができるようにします。

【関数の返り値】 return words;
ここは関数内で単語リストの入れ物、List<string> wordsを作り、スクレイピングでその中身を入れた後で最後にreturnでリストが返ってくるようにします。

このGetWordList関数はMainメソッドの後に書きますが、後で掲載する完成形を見てどんな形で関数を入れるかはそこで説明します。

ステップ2:単語リストからランダムに単語一個を選択

これはすでに書いたコードですので説明は省きます。一点変更したのは、最後にConsole.WriteLine()を入れてコンソール上の表示で一行空けるようにした点です。スペースを空けて見やすくするだけの目的です。

//================================
//Step 2:単語リストからランダムに一つ選ぶ
//================================
Random r = new Random();
string word = words[r.Next(0, words.Count - 1)];

Console.WriteLine("次の単語の例文を表示します:" + word);
Console.WriteLine(); //一行空ける

ステップ3:単語が入った例文を表示させる

さあ、ここからが例文スクレイピングの関数を呼び出すコードです。まずはじっくりみて自分で理解してみてください。

 //=================================================
 //Step 3: 選んだ単語の例文をGetSampleSentencesメソッドで表示させる
 //===============================================

 //スクレイプ関数、GetSampleSentencesを呼ぶ
 var samples = GetSampleSentences(word);

 //例文リストの中身を一つずつ表示する
 foreach (var sample in samples)
 {
     Console.WriteLine(sample.Key); //英語の表示
     Console.WriteLine(sample.Value); //日本語の表示

     //一行空ける
     Console.WriteLine();
 }

ここでDictionaryデータが登場していますが、これは『今すぐ書ける 1分間プログラミング』で作ったWikipediaクイズアプリの中でじっくり解説していますので、その使い方はそちらをご覧ください。

ここではGetSampleSentences関数を呼び出し、ランダムに選択された単語を渡しています。そして出てきた例文リストは英語と日本語の両方が入ったDictionary型のsamplesに入ります。そして英語はkey、日本語はvalueで取り出し、一つひとつループで画面表示しています。ここでも間に一行スペースを入れるようにしています。

では注目の例文スクレイピング関数、GetSampleSentencesの中身を見ていきます。

       /// <summary>
       /// 例文スクレイピングをする関数
       /// </summary>
       /// <param name="word">取得する例文の対象となる単語</param>
       /// <returns></returns>
       private static Dictionary<string, string> GetSampleSentences (string word)
       {
           //英語例文と訳文を入れておくDictionaryデータを作る
           var samples = new Dictionary<string, string>();

           //================================
           //Webサイトを開いてHTMLを読む。
           //================================

           //weblioのURLを変数urlに入れる
           string url = string.Format(@"https://ejje.weblio.jp/sentence/content/{0}/", word.ToLower());
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

           //================================
           //例文のパターンを抽出する
           //================================

           //「なんちゃら」パターンに合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<p class=qotCE>(.+?)<\/p>.*?<p class=qotCJ>(.+?)<span>.+?<\/span>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

           //合致したものをすべて表示する
           foreach (Match match in matches)
           {
               //英語のpタグには多くのゴミタグがあるのでそれをすべて取り除く
               string english = Regex.Replace(match.Groups[1].Value, "<.+?>", "", RegexOptions.IgnoreCase);
               //さらに「例文帳に追加」というテキストが残るのでそれも除去
               english = english.Replace("例文帳に追加", "");

               //日本語はゴミ無しでそのまま取得できる
               string japanese = match.Groups[2].Value;

               //Dictionaryに入れる前に重複した英文がないかどうかをチェックする
               if (!samples.ContainsKey(english))
               {
                   //英語と日本語両方をDictionaryに入れる
                   samples.Add(english, japanese);
               }
           }

           //例文リストを返す
           return samples;

       }

ポイントは次の3つです。

❶ 例文リストはDictionaryに入れる

これは英文と訳文を対にして入れるのでDictionaryを使いました。Dictionaryで気を付けるのはKayValuePariでKeyが重複できないということ。つまり同じ英語の例文を入れることができません。もしかすると同じ例文で違う訳文が存在するかもしれませんが、その場合は最初のものだけ入れるようにします。

❷ Weblioから例文を抽出する正規表現

var matches = Regex.Matches(html, @"<p class=qotCE>(.+?)<\/p>.*?<p class=qotCJ>(.+?)<span>.+?<\/span>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

これが難関点です。どのような「なんちゃら」パターン(Part4で詳細を解説しています)になっているかじっくり説明します。

英語は:<p class=qotCE>なんちゃら</p>
日本語は<p class=qotCJ>なんちゃら<span>なんちゃら</span></p>

英語例文と訳文はひと塊になっているのでこのパターン全部を一気に正規表現にして対応します。

<p  class=qotCE>(.+?)</p>.*?<p class=qotCJ>(.+?)<span>.+?</span></p>

最初の(.+?)は英語例文にマッチしますが、ここのpタグにはいっぱい余計なタグが入っていました(Part4で解説)。なので、ここはマッチしたテキストを持ってきた後でゴミタグを除去する必要があります。詳しくは後でまた解説します。まずはここで英語の例文を抽出しているということを理解してください。

次の.*?は何でしょう?これは英語のpタグセクションと日本語のpタグセク所の間に改行とかが入っているため、<p>なんちゃら</p><p>なんちゃら</p>としてパターンを探すとマッチしなくなります。ただ、HTMLというのはいい加減なもので、そうした改行のようなものは入れても入れなくても表示にはなにも影響しません。なので「入っているかもしれないし、入っていないかもしれない」という対処が必要です。*を使ったのもそれが理由です。これでPタグの間に何があろうとマッチされます。

その次は日本語のpタグの部分ですが、最初の.+?が日本語に当たる部分ですので、ここに括弧を入れてマッチさせます。後半は括弧を付けないのは、こnspanのテキストは不要なので「無視」するだけです(ここもPart4で解説)。

この正規表現は一気に英語と日本語をマッチさせているのでちょっと高度ですが理解できましたか?

最後に、ここのRegexの呼び出しで、最後にRegexOptions.SingleLineというのが入っています。これは簡単に言うと「テキストに改行が入っていて複数行になっていてもマッチさせる」という意味で、長いテキストを対象にしたマッチングには必要なオプションなのでこれは覚えておくと便利です。

さて、英語の例文部分と日本語訳部分のマッチングができたら結果をsamplesに入れますが、その前にクリーンアップする作業が必要です。英語は例のゴミタグ除去と、さらに「例文帳に追加」というタグの間にあった定型のテキストもあるのでその除去も必要です。一方、日本語は括弧でマッチさせた部分にはごみはないのでそのまま使えます。

//英語のpタグには多くのゴミタグがあるのでそれをすべて取り除く
string english = Regex.Replace(match.Groups[1].Value, "<.+?>", "", RegexOptions.IgnoreCase);
//さらに「例文帳に追加」というテキストが残るのでそれも除去
english = english.Replace("例文帳に追加", "");

//日本語はゴミ無しでそのまま取得できる
string japanese = match.Groups[2].Value;

画像4

「TOEIC英単語1000本ノック」アプリVer1の完成形

これでVersion 1のコーディング作業は完了です。完成形はこれです。

using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace WebScrapingPart2
{
   class ProgramPart2
   {
       static void Main(string[] args)
       {
           //================================
           //GetWordListメソッドで単語リストを取得
           //================================
           var words = GetWordList();

           //================================
           //単語リストからランダムに一つ選ぶ
           //================================
           Random r = new Random();
           string word = words[r.Next(0, words.Count - 1)];

           Console.WriteLine("次の単語の例文を表示します:" + word);
           Console.WriteLine(); //一行空ける

           //=================================================
           //選んだ単語の例文をGetSampleSentencesメソッドで表示させる
           //===============================================

           //スクレイプ関数、GetSampleSentencesを呼ぶ
           var samples = GetSampleSentences(word);

           //例文リストの中身を一つずつ表示する
           foreach (var sample in samples)
           {
               Console.WriteLine(sample.Key); //英語の表示
               Console.WriteLine(sample.Value); //日本語の表示

               //一行空ける
               Console.WriteLine();
           }

           Console.ReadLine();
       }

       /// <summary>
       /// 英単語リストをスクレイピングで取得する。TOEIC英単語のみサポート
       /// </summary>
       /// <returns></returns>
       private static List<string> GetWordList ()
       {
           //単語リストの変数を作る
           var words = new List<string>();

           //================================
           //Webサイトを開いてHTMLを読む。
           //================================

           //WordHippoのURLを変数urlに入れる
           string url = @"https://toiguru.jp/toeic-vocabulary-list";
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

           //================================
           //単語のパターンを抽出する
           //================================

           //<td>なんちゃら<br>なんちゃら</td>に合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<td>(.+?)<br>(.+?)</td>");
           //合致したものをすべて表示する
           foreach (Match match in matches)
           {
               //英語を単語リストに加える
               words.Add(match.Groups[1].Value);
           }

           return words;
       }

       /// <summary>
       /// 例文スクレイピングをする関数
       /// </summary>
       /// <param name="word">取得する例文の対象となる単語</param>
       /// <returns></returns>
       private static Dictionary<string, string> GetSampleSentences (string word)
       {
           //英語例文と訳文を入れておくDictionaryデータを作る
           var samples = new Dictionary<string, string>();

           //================================
           //Webサイトを開いてHTMLを読む。
           //================================

           //weblioのURLを変数urlに入れる
           string url = string.Format(@"https://ejje.weblio.jp/sentence/content/{0}/", word.ToLower());
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

           //================================
           //例文のパターンを抽出する
           //================================

           //「なんちゃら」パターンに合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<p class=qotCE>(.+?)<\/p>.*?<p class=qotCJ>(.+?)<span>.+?<\/span>", RegexOptions.IgnoreCase | RegexOptions.Singleline);

           //合致したものをすべて表示する
           foreach (Match match in matches)
           {
               //英語のpタグには多くのゴミタグがあるのでそれをすべて取り除く
               string english = Regex.Replace(match.Groups[1].Value, "<.+?>", "", RegexOptions.IgnoreCase);
               //さらに「例文帳に追加」というテキストが残るのでそれも除去
               english = english.Replace("例文帳に追加", "");

               //日本語はゴミ無しでそのまま取得できる
               string japanese = match.Groups[2].Value;

               //Dictionaryに入れる前に重複した英文がないかどうかをチェックする
               if (!samples.ContainsKey(english))
               {
                   //英語と日本語両方をDictionaryに入れる
                   samples.Add(english, japanese);
               }
           }

           //例文リストを返す
           return samples;

       }

   }
}

Mainメソッドの後にGetWordList関数とGetSampleSentences関数があるのはわかりますか?こうやって一定の処理を関数の入れ物にいれてまとめておくとコード全体がわかりやすくなるだけではなく、関数を変更する(例えば別のテスト用の英単語をスクレイプしたり、条件に合った例文だけを取得するなど)時には関数の中身だけ変更すれば即対応できます。もちろん一度作った関数をライブラリなどにまとめておくと別のプログラムで使うこともできます。「関数の入れ物に処理をまとめる」というのはたくさんの利点があるのです。

ではこのVersion1はスタートさせるとこうなります。下の例ではconvince(納得させる)という単語が選ばれ、その後に例文がずらーっと出てきます。皆さんがアプリをスタートさせたら恐らく別の単語が出てくると思いますが、数多くの例文が出てくるのは同じはずです。

ちなみに、党税なのですが単語も例文もダイナミックにネットからスクレイピングで取得するので、アプリをスタートさせるにはネットワークに接続されていないとエラーになります(当然ですがエラー処理などは一切していません)。

皆さんんのほうでも何度かアプリを立ち上げて、その都度どんな単語が出てきて、どのような例文が表示されるかチェックしてみてください。

画像2

ではVersion 2はどうする?

このV1ではスクレイピングの基本はカバーできました。

❶ TOEIC英単語をスクレイピングで取得する
❷ ランダムに1個選ぶ
❸ 選んだ単語の例文を表示させる

ただし、これだけでは「学習アプリ」としてはほとんど機能していません。もっと便利で有効なものにしていくには、これをどう変更させていくかが重要です。

ここで大切なのは「皆さんはどうやって英単語を学習したいか」という”要望”です。自分のためにアプリを作っているわけなので、どんな仕様にしても構いません。完全自己満足で考えてみください。私だったら次のような機能を加えたいですね。

① 自分が知っている単語はスキップしたい
② 「よい例文」に絞って見せてほしい
③ TOEICだけでなくSATなど別の単語リストも扱いたい
④ 黒と緑の画面はそっけないので文字をもっとカラフルに分かりやすく
⑤ もっとたくさんの例文サイトからサンプルを集めたい
⑥ 例文を一個一個みたい。まずは英語、Enterを押すと日本語を表示
⑦ 自分でシャドーイングしたい。声を認識して発音もチェックしたい
⑧ 英文はナイスな発音で読み上げてほしい
⑨ 英語の発音は繰り返し聞けるようにする
⑩ 逆に日本語を見て英語を話すトレーニングをしたい

もう言いたい放題ですが、自分が十分に満足するものにしないと意味がありません。目標は高く持って最高のものにしたいものです。

これから『 1分間プログラミング』方式でちょっとずつ完成させていきます。もちろん皆さんが独自に欲しい機能があればそれを入れることも簡単にできます。皆さん自身がエンジニアになってアプリを作っているわけなので、どうぞ好きなアプリに仕上げてください。

次回のPart6では

Version 1を修正・拡張していきます。上記10の拡張仕様で重要度の高いものから対処していきます。引き続き乞うご期待!


この記事が気に入ったらサポートをしてみませんか?