@nqounetです。
前回は、WebScraperという基底クラスを作成し、scrapeメソッドで処理の骨格を定義しました。今回は、この基底クラスを継承して、ニュースサイト用と天気予報サイト用のスクレイパーを作成します。
このシリーズについて
このシリーズは「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了した方を対象に、実践的なWebスクレイパーを作りながらオブジェクト指向設計を深く学ぶシリーズです。
シリーズ全体の目次は以下をご覧ください。
継承とオーバーライドについての基礎は、以下の記事で解説しています。
NewsScraperクラスを作成する
まず、ニュースサイト用のスクレイパーを作成しましょう。extendsを使ってWebScraperクラスを継承し、抽象メソッドをオーバーライドします。
| |
このクラスでは、基底クラスでdieになっていたextract_dataとsave_dataを、ニュースサイト用の具体的な処理でオーバーライドしています。
WeatherScraperクラスを作成する
次に、天気予報サイト用のスクレイパーを作成します。
| |
天気予報スクレイパーでは、extract_dataで日付・天気・気温をハッシュリファレンスとして返し、save_dataでわかりやすく整形して表示しています。
クラスの継承関係を図解する
ここまでで作成したクラスの継承関係を図にすると、以下のようになります。
classDiagram
class WebScraper {
+url
+scrape()
-_fetch_html()
+extract_data(dom)*
+save_data(data)*
}
class NewsScraper {
+extract_data(dom)
+save_data(data)
}
class WeatherScraper {
+extract_data(dom)
+save_data(data)
}
WebScraper <|-- NewsScraper : extends
WebScraper <|-- WeatherScraper : extends
note for WebScraper "* = 抽象メソッド"
WebScraperを継承したNewsScraperとWeatherScraperが、それぞれ独自のextract_dataとsave_dataを実装しています。
実際に動かしてみる
これらのクラスを使ってスクレイパーを動かすスクリプトを作成しましょう。
| |
このスクリプトをrun_scrapers.plとして保存し、実行します。
| |
どちらも正常に動作しました!
何が起きているのか?
ここで、$news->scrape()を呼び出したときに何が起きているかを詳しく見てみましょう。
sequenceDiagram
participant Main as メインスクリプト
participant NS as NewsScraper
participant WS as WebScraper
Main->>NS: scrape()
Note right of NS: NewsScraperには<br/>scrapeがないので<br/>親クラスを探す
NS->>WS: scrape() [継承]
WS->>WS: _fetch_html()
WS->>NS: extract_data(dom)
Note right of NS: NewsScraperの<br/>extract_dataが呼ばれる
NS-->>WS: @headlines
WS->>NS: save_data(@headlines)
Note right of NS: NewsScraperの<br/>save_dataが呼ばれる
NS-->>Main: 完了
NewsScraperにはscrapeメソッドがないので、親クラスWebScraperのscrapeが呼ばれるscrape内で$self->extract_data($dom)が呼ばれる$selfはNewsScraperのインスタンスなので、NewsScraperのextract_dataが呼ばれる- 同様に、
$self->save_data(@data)ではNewsScraperのsave_dataが呼ばれる
このように、親クラスで定義した処理の流れ(scrape)の中で、子クラスで定義した具体的な処理(extract_data, save_data)が呼び出される仕組みになっています。
コピペと比較する
最初に書いたコピペだらけのスクリプトと、今回のクラス設計を比較してみましょう。
| 観点 | コピペアプローチ | クラス継承アプローチ |
|---|---|---|
| 共通処理の場所 | 各スクリプトに分散 | 基底クラスに集約 |
| 新しいサイトの追加 | ファイル全体をコピペ | extendsして2メソッド実装 |
| 共通処理の修正 | 全ファイルを修正 | 基底クラスだけ修正 |
| コードの見通し | ファイルごとに異なる | 統一された構造 |
明らかにクラス継承アプローチの方が保守性が高いですね。
今回のまとめ
今回は以下のことを学びました。
extendsを使って基底クラスを継承する- 抽象メソッド(
extract_data,save_data)をオーバーライドして具体的な処理を実装する - 親クラスの
scrapeメソッドから、子クラスのオーバーライドしたメソッドが呼び出される
次回予告
今回作成したsave_dataメソッドは、ニュースもお天気も「画面に表示」するだけでした。でも、天気データはファイルに保存したいこともありますよね。
次回は、「デフォルトの保存処理」を基底クラスに用意し、必要に応じてサブクラスでカスタマイズできるようにする方法を学びます。これは「Hook Methods(フックメソッド)」と呼ばれるテクニックです。
お楽しみに!
