見出し画像

Webスクレイピング Part 2 - プログラミングで英語の例文をごっそり抽出する!

前回Part1では、Webスクレイピングツールを使って特定のWebページからデータを取り出す作業を説明しました。具体的なタスクは、TOEICに出てくる頻出単語を掲載されたページから単語リストを取り出すというものです。この場合はWebサイトの中身はほとんど変わらないために、抽出したデータ(この場合は単語リスト)はどこかに保存しておけばよく、一回の抽出作業で完了します。なのでツールを使うのが有効でした。

ところが、ターゲットとなるサイトの中身が条件によって変わる場合はどうでしょう。例えば、オークションサイトに出品されている商品の値段をすくレイプして、その状況やトレンドを調べたいという場合です。この場合の条件は「日時」です。一回抽出しただけではその時点の値段がわかるだけで、数分後にはまたビットの値段が変わるかもしれません。だからといって毎分スクレイピングツールを走らせるわけにもいきません。

日時の場合はスクレイピングのタイミング(いつ抽出するか)の問題なので、ツールによっては繰り返し呼び出す機能をサポートしたものもあります。では次の場合はどうでしょう。

Part1で抽出した英単語リストをもとに、そこからランダムに単語を選ぶ。選ばれた単語をもとに英語の例文を抽出したい。

この場合の条件は「単語」です。しかし、アプリなどを作っていてどの単語が選ばれるかはユーザー次第です。英単語によってその場で抽出しないといけません。「全部スクレイプしておいて保存しておけばいいんじゃない?」と思うかもしれませんが、そんなに大量のデータをスクレイプするのは問題です。相手のサイトにも負荷がかかりますし、そもそも単語の数が何千もあるとデータが大きくなりすぎます。この場合は単語が選ばれるごとに対象となるページだけをスクレイプするべきです。

画像10

今回やるのがこういった条件に従ってスクレイプする「動的スクレイピング」です。

そもそも英語の例文サイトってどこにあるの?

まずスクレイプを考える前に、どのサイトをターゲットにするか理解しておかないといけません。これは意外と簡単です。検索サイトでsample sentencesみたいなキーワードで探すと結構たくさん出てきます。いくつか例を挙げてみます。

これらは辞書系のサイトで、当然ですが辞書サイトには単語の意味に添えて必ず例文が掲載されています。ただ、Words in a Sentenceのように例文に特化したサイトなどもあり、詳しく探せば相当な数が見つかるはずです。なのでターゲットを見つける苦労はありません。

上記3つは英語のサイトですが、日本の辞書系サイトも良質なサンプルを提供しています。Weblioはそのよい例です。

日本語の辞書サイトだと日本語の意味も抽出できるので日本人にとっては使いやすいでしょう。

プログラミングを書いて対処する

さて、ここからはプログラミングの話に入ってきます。もちろん経験のない方には無理難題なのですが、本格的にWebスクレイピングをやるにはどうしてもコーディング作業が不可欠です。これを機に是非とも挑戦してみてください。

プログラミング経験ゼロからどうやって取り組んでいけばよいか、詳しくまとめた本を出版しました(6月19日発売)。

実はスクレイピングに必要なコーディング知識は結構限られていますので、プログラミングを始める動機としてはとてもよいテーマです。これから説明するスクレイピングのコーディングはこの本を読む程度で十分始められるものなので、是非是非チャレンジしてみてください。

「正規表現」と「XPath」の2つを覚えるとほぼどんなWebサイトからでもスクレイピングができる!

いきなり難解な言葉がでてきましたが、この2つは”ツール”みたいなものだと考えてください。それがどんなことをするものなのかを理解する前に、そもそもスクレイピングというのはどうすることなのかをじっくり理解していきましょう。ここでは「英語の例文が掲載されたサイトから例文をごっそり取り出す」のが目的でした。それをどんなアプローチでやっていけば考えてみます。

画像1

まずはサイトの”すっぴん顔”、HTMLを見てみる

WebサイトのページはHTMLというコードで書かれていることは聞いたことがあると思います。残念ながらここでは詳しく解説できませんが、スクレイピングをするだけならHTMLの深い理解は不要です。まずは実際どんなものかを見てみると大体わかると思いますので、例文サイトの一つ、WordHippoで、"critical"(深刻な、重要な)という単語が入った例文を表示させたページを見てみます。

皆さんも次の手順でそのページを開いてみて下さい。

https://www.wordhippo.com/ をブラウザで開く。
❷ 検索フィールドにcriticalを入力して[Find It] をクリック。
❸ [Sentences]というタブをクリック。

画像2

ぱっとみて分かると思いますが、criticalが入った例文がたくさん掲載されています。この例文全部をごっそり抽出しようというのが今回のミッションです。

まずはターゲットとなるページを開くことができましたので、実際の作業をステップに分けて進めていきます。

ステップ1:HTMLパターンを見つける

まずはこのページのHTMLを見てみます。お使いのブラウザがEdgeでもChromeでも似たような作業ですので、ここではEdgeを使って解説します。

ページ上のどこか白い空白のところで右クリックし、「開発者ツールで調査する」を選ぶ。

画像3

すると検証ツールの画面が右サイドに現れます(上の画面では黒くなっていますが、皆さんのブラウザでは白かもしれません)。

左が実際のページ、そして右側がそのHTMLという、実際のページの裏側のテキストを表示しています。知らない人には驚きですが、Webページのカラフルなデザインや画像、ビデオなど、実はすべてこのHTMLというコードによって書かれているのです。この”すっぴん顔”をじっくり見ていくのがスクレイピング作業の第一段階なのです。

なお、Chromeを使っていると検証ツールが上下に分かれて表示されることがありますが、左右表示にしたい場合は、検証ツールの右上部に三つの点アイコンがあり、そこをクリックするとDock Side(ドッキング)という項目があるので、そこから左右にするアイコン(一番右)を選んでください。

画像4

❷ 例文の一つを選んでじっくりHTMLを分析する

まずは例文の一つを選んでそのHTMLがどうなっているのか見てみます。検証ツールの左上に「ページ上の’検査対象となる要素を見る」というアイコンがあります。これをクリックすると、左のページ内で文章を選択すると、それに対応するHTMLテキストが右側でハイライトされて示されます。まずはこれを使って、最初の例文にカーソルを持って行ってみてください。

画像5

一度クリックすると、左の実ページ上での選択に応じて、右のHTLMコード内で対応する行がハイライトされます。これを利用して、まず一番最初の例文、”We need to look at these proposed changes with a critical eye before we accept them.”を選択してみてください。

画像6

上のスクリーンのように、左サイドで例文の一番最初のものにカーソルを持っていってクリックすると、右側のHTMLのほうで対応する部分がハイライトされます。

ここで、ハイライトされた右サイトの行をじっくりみてみます。

【実際のテキスト】

画像8

【対応するHTMLのテキスト】

画像7

❸ HTMLのパターンを見つけ出す

ここからがスクレイピングの醍醐味です!HTMLを知らなくても十分理解できるので、がんばってついてきてください。

この例文テキストでは検索した単語、criticalが太字になっています。HTMLをみてみるとcriticalがこんな感じになって表記されています。

<b>critical</b>

この”くの字括弧”で書かれた<b>というのをタグと呼びます。タグはテキストのある部分からある部分までを指定して、そのテキストの表示方法を指定します。ここの bというタグはBold (b)タグと言ってタグで挟まれた部分を太字にします。実際にそうなってますよね。ちなみにタグ指定の終わりのほうには</b>のようにスラッシュが入ります。

では同じように文章全体をみてみると、そこは<td>...</td>(テーブルデータ)タグで囲まれています。つまり、このサイトでは例文を次のようなパターンで表記しているようです。

<td> .... 文章 ... <b>単語</b> ......文章... </td>

<td>We need to look at these proposed changes with a <b>critical</b> eye before we accept them.</td>

まずは<td>と</td>で挟まれたところが例文全体のようですね。他の例文もみてみるとすべて同じようになっているみたいです。結構単純なパターンですよね。でもこの「〇〇〇というパターンになっているテキストを抽出する」ってどうやるのでしょうか。

画像11

ステップ2:ベースとなるコードを書いてみる

今回のスクレイピングでは「正規表現」という技術を使ってデータを抽出します。ただ、パターン抽出のための正規表現はとりあえず置いておいて、まずはそこに至るまでのコードを書いてみます。対象のWebページからHTMLテキストを読み込むコードを書いてみて、その後で正規表現に入っていきます。

① Visual Studio 2019でコンソールアプリ(.Net Core)のプロジェクトを作る

ここでは『今すぐ書ける 1分間プログラミング』でのコーディングと同様、Visual StudioC#のコードを書いていきます。Visual Studioという開発ツールを使ったことがない方は、プロジェクトの作り方など詳しく解説していますので、是非この本を参考にしてみてください。.

ではプロジェクトを作る際には、.Net Coreコンソールアプリを選んでください(注:.Net Frameworkのコンソールアプリではありません)。プロジェクトを作ると、まずはこのようなMainメソッドが作られます。当初から”Hello World"を表示される行が一行入っていますがそれは削除してください。

using System;
namespace WebScrapingPart2
{
   class Program
   {
       static void Main(string[] args)
       {
           ここにコードを書いていきます。
       }
   }
}

② WebサイトからHTMLテキストをロードする

まずは次のようなコードを書いてみてください(コピペできます)。Mainに書くコードはたったの5行(二つのスラッシュでコメント行は除きます)。これでcriticalの例文サイトに対するすべてのHTMLテキストを取得できます。

using System;
using System.Net; //新しくこれを入れる

namespace WebScrapingPart2
{
   class Program
   {
       static void Main(string[] args)
       {
           //================================
           //Webサイトを開いてHTMLを読む。
           //================================
           //WordHippoのURLを変数urlに入れる
           string url = @"https://www.wordhippo.com/what-is/sentences-with-the-word/critical.html";

           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();

           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

           //読み込んだhtmlを表示する
           Console.WriteLine(html);

           Console.ReadLine();
       }
   }
}

このコードを実行すると以下のように、コンソール画面が現れて、そこにHTMLテキストがどーっと表示されいます。

画像9

いきなりドドーっとテキストが表示されてしばらく続くので何か壊れたのかと不安になるかもしれませんが、最後までいけばピタッと止まります。「こんな大量のコードが裏にあるの?」と驚くかもしれませんが、実はあのシンプルなページでもこんな大量のコーディング作業の上に成り立っているのです。

さて、コードのポイントは次の2点です。

● ウェブページを読むにはWebClientというクラスを利用する
 ⇒ そのためにusing System.Net; という行をパッケージの読み込みに加える
● 読み込みにはDownloadStringというメソッドにURLを入れる

たったこれだけです。

ステップ3:正規表現で例文を抽出する

さて、htmlという変数にはこのサイトのHTMLテキストがすべて入っているので、あとは次のようなコードが書けると完成です。

「html変数から<td>なんちゃら</td>となっているところをすべて取ってくる」

さて、これを実現してくれるのが正規表現です。英語でRegular Expressionと言い、C#ではRegexというクラスが用意されています。これを使ってコードを書いてみます。追加部分、「例文のパターンを抽出する」以下、3行を加えてください。また、新しいクラスRegexを使うのでusing System.Text.RegularExpression;を追加します。

using System;
using System.Net;
using System.Text.RegularExpressions; //これを追加

namespace WebScrapingPart2
{
   class Program
   {
       static void Main(string[] args)
       {
           //================================
           //Webサイトを開いてHTMLを読む。
           //================================

           //WordHippoのURLを変数urlに入れる
           string url = @"https://www.wordhippo.com/what-is/sentences-with-the-word/critical.html";
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

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

           //<td>なんちゃら</td>に合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<td.+?>.+?</td>");

           //合致したものを一つひとつ表示する
           foreach (Match match in matches) 
           {
               Console.WriteLine(match.Value); ; ;
           }

           Console.ReadLine();
       }
   }
}

ここでキモとなるのはこの一行です。

//<td>なんちゃら</td>に合致したテキストを取り出す
var matches = Regex.Matches(html, @"<td.+?>.+?</td>");

最初の例文のHTMLをもう一度見てみます。

<td id="exv2st6495827">We need to look at these proposed changes with a <b>critical</b> eye before we accept them.</td>

最初のtdのタグの中にidというデータ(ここでは「属性」と呼びます)が入っているので「なんちゃら」方式を正確に言えば、

<td なんちゃら>なんちゃら</td>

これが例文のパターンです。そこで、この「なんちゃら」の部分を".+?" に置き換えると正規表現ができてしまいます。

<td.+?>.+?</td>

このパターンを正規表現で処理すとこうなります。

var matches = Regex.Matches(html, @"<td.+?>.+?</td>", RegexOptions.IgnoreCase);

これはhtml変数に入っているテキストをズズーっとスキャンして、例の「なんちゃら」パターンに当てはまるところをすべてゲットします。結果はmatchsという変数に入り、その中にマッチした例文がすべて入っているはずです。foreachループでひとつづつアウトプットしてみるとこうなりました。

<td class="label">What's another word for </td>
<td class="submitcell"><input class="button" type="submit" value="find it"/></td>
<td class="label">What's the opposite of </td>
<td class="submitcell"><input class="button" type="submit" value="find it"/></td>
<td class="label">Meaning of the word </td>
・・・

なんだか肝心の例文が見当たりません。どうやら<td>なんちゃら</td>にマッチするのは例文の箇所以外にもいっぱいあるようです。これはすべてゴミですよね。もうちょっと正確にマッチングさせないといけないようです。

そこで、例文には必ず対象の単語を太字にするために<b>...</b>で囲んでいた点に注目します。これが入っているものだけ抽出するようにしてみましょう。正規表現を次のように変えてみます。

//<td>なんちゃら</td>に合致したテキストを取り出す
var matches = Regex.Matches(html, @"<td.+?>.*?<b>.+?</b>.+?</td>");

あれ?Bタグの前の「なんちゃら」が.+?ではなく.*?となっていますが+と*何が違うかは後で説明します。まずはこの通りやってみてください。

<td id="exv2st6495827">We need to look at these proposed changes with a <b>critical</b> eye before we accept them.</td>
<td id="exv2st6495818">This first comprehensive and <b>critical</b> survey of his life and work soon became the standard text on the architect and polyhistor.</td>
<td id="exv2st6495815">This amount of <b>critical</b> energy is known as the activation energy of the reaction.</td>
・・・・

おおっ、どうやら大成功のようです。実際このmatchesに入っている項目の数は300.つまり”critical"という単語が入った例文が300もゲットできたわけです。すごい!

画像12

ステップ4:ごみをきれいにしてアウトプットする

後は余計なtdとかbとかのタグを取り除くだけです。ここではごみはタグだけのようなので、次のようなコードで対処できます。

string sample = Regex.Replace(match.Value, @"<.+?>", "");

つまり、Replaceというメソッドを使い、”<なんちゃら>”に当てはまるものはすべて空白(””)に入れ替えるということです。正規表現はこの「なんちゃら」パターンにあてはまるものは簡単に作ることができます。だいぶノリが分かってきたと思います。

最終コードはこれです。

using System;
using System.Net;
using System.Text.RegularExpressions;

namespace WebScrapingPart2
{
   class Program
   {
       static void Main(string[] args)
       {
           //================================
           //Webサイトを開いてHTMLを読む。
           //================================

           //WordHippoのURLを変数urlに入れる
           string url = @"https://www.wordhippo.com/what-is/sentences-with-the-word/critical.html";
           //HTMLを読み込むWebClientを作る
           var wc = new WebClient();
           //WebClientのDownloadStringメソッドでHTMLを読み込む
           string html = wc.DownloadString(url);

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

           //<td>なんちゃら</td>に合致したテキストを取り出す
           var matches = Regex.Matches(html, @"<td.+?>.*?<b>.+?</b>.+?</td>");
           //合致したものをすべて表示する
           foreach (Match match in matches)
           {
               //余計なタグを取り除く
               string sample = Regex.Replace(match.Value, @"<.+?>", "");
               Console.WriteLine(sample);
           }; ;

           Console.ReadLine();
       }
   }
}

これできれいに例文が取り出せました!

We need to look at these proposed changes with a critical eye before we accept them.
This first comprehensive and critical survey of his life and work soon became the standard text on the architect and polyhistor.
This amount of critical energy is known as the activation energy of the reaction.
Action potential generation in inhibitory interneurons is critical for cortical excitation-inhibition balance.
A few weeks ago, she debuted her spring collection of breathtakingly short, tight dresses and skirts, to critical raves.
This is a critical system that leaves you one failure away from catastrophe, as in this case.
Now he is in critical condition, facing the risk of a coma and cardiac arrest that could end his life.
・・・

例文を抽出する単語を自由に指定したいのですけど…

さて、最低限の抽出はできました。作業を振り返ると、

● WebClientクラスを使ってHTMLテキストを取得した
●  Regexクラスを使ってパターンに当てはまるテキストを抽出した
● さらに余計なテキストを取り除いてきれいな文章に変えた

ただ、これはcriticalという単語を決め打ちしてやったもの。他の単語の例文を抽出する場合はコードのなかで変更することが必要です。どこかというとURLの箇所です。

⇒ www.wordhippo.com/what-is/sentences-with-the-word/critical.html

このcriticalを別の単語にすり替えるだけですが、もうすこしスマートにしたいですよね。そこでPart 3では自分で自由に単語を指定して例文を抽出するようにするほか、Part1で抽出した英単語リストを利用して、もうすこしアプリっぽくコードを書いてみます。乞うご期待!


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