PHPでDOMDocumentを使ってスクレイピング

(1/1)
Webサイトから情報を抽出することをスクレイピング(scraping)と呼ぶ。いままで紹介してきたPHPプログラムでは、正規表現などを使ってスクレイピングを行ってきた。
PHPバージョン5以上では、HTMLやXMLドキュメントをオブジェクトとして扱うことができる DOMDocumentクラスが用意されている。
そこで今回は、DOMDocumentクラスを使ってスクレイピングを行ってみることにする。他のプログラムでも利用できるよう、DOMDocumentクラスを利用する形のユーザー・クラス pahooScraping を用意した。

(2023年9月18日)pahooScrapingクラスに変更

サンプル・プログラムの実行例

PHPでDOMDocumentを使ってスクレイピング

目次

サンプル・プログラム

圧縮ファイルの内容
DOMscraping.phpサンプル・プログラム本体。
pahooScraping.phpRSS処理に関わるクラス pahooScraping。
スクレイピング処理に関わるクラスの使い方は「PHPで作るRSSビューア」を参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
DOMscraping.php 更新履歴
バージョン 更新日 内容
4.0.1 2023/09/29 bug-fix
2.0.0 2023/09/18 pahooScrapingクラスに変更
1.1 2021/05/08 PHP8対応,リファラ・チェック改良
1.0 2015/10/20
pahooScraping.php 関数/メソッド一覧
関数/メソッド 機能 詳細
class::pahooScraping
__construct コンストラクタ
__destruct デストラクタ
isJapanese 使用言語が日本語かどうかを判定する
setLanguage 使用言語を設定する.
isError エラーが発生しているかどうかを取得する.
getError エラー番号を取得する.
getErrorMessage エラー・メッセージを取得する.
resetError エラー状態をリセットする.
isphp8over PHP8以上かどうか検査する
evalXPath 指定したXPath式を評価し,結果を返す.
queryXPath 指定したXPath式を評価する.
getTitle titleを取得
getDescription descriptionを取得
pahooInputData.php 更新履歴
バージョン 更新日 内容
1.5.0 2024/01/28 exitIfExceedVersion() 追加
1.4.2 2024/01/28 exitIfLessVersion() メッセージ修正
1.4.1 2023/09/30 コメントの訂正
1.4.0 2023/09/09 $_GET, $_POST参照をfilter_input()関数に置換
1.3.0 2023/07/11 roundFloat() 追加

解説:初期化処理

  29: //プログラム・タイトル
  30: define('TITLE', 'DOMDocumentを使ってスクレイピング');
  31: 
  32: //データ入力に関わる関数群:include_pathに配置すること
  33: require_once('pahooInputData.php');
  34: 
  35: //PHPバージョン・チェック
  36: exitIfLessVersion(MINUMUM_VERSION);
  37: 
  38: //リファラチェック+リリースフラグの設定
  39: if (isset($_SERVER['HTTP_HOST']) && ($_SERVER['HTTP_HOST'] == 'localhost')) {
  40:     define('FLAG_RELEASE', FALSE);
  41:     define('REFER_ON', '');
  42:     ini_set('display_errors', 1);
  43:     ini_set('error_reporting', E_ALL);
  44: else {
  45:     //リリース・フラグ(公開時にはTRUEにすること)
  46:     define('FLAG_RELEASE', TRUE);
  47:     //リファラ・チェック(直リン防止用;空文字ならチェックしない)
  48:     define('REFER_ON', 'www.pahoo.org');
  49: }
  50: 
  51: //表示幅(ピクセル)
  52: define('WIDTH', 600);
  53: 
  54: //スクレイピングURL(デフォルト)
  55: define('DEF_URL', 'https://www.pahoo.org/e-soul/webtech/php02/php02-41-01.shtm');
  56: 
  57: //スクレイピング処理に関わるクラス:include_pathが通ったディレクトリに配置
  58: require_once('pahooScraping.php');

各種定数・変数は、変更不可という記載がないかぎり、自由に変更可能である。

関数  require_once  を使って、スクレイピング処理を行うクラス pahooScraping.php を読み込む。include_pathが通ったディレクトリに配置しておくこと。

解説:pahooScrapingクラスとDOMDocumentクラスの使い方

  13: class pahooScraping {
  14:     private $dom;           //DOMDocument
  15:     private $xpath;         //DOMXPath
  16:     private $language;      //使用言語(初期値は日本語)
  17:     private $error;         //エラー・メッセージ
  18:     private $errorTable;    //エラー番号 => エラー・メッセージ 一覧
  19: 

  20: /**
  21:  * コンストラクタ
  22:  * @param   string $contents 解析対象テキスト
  23:  * @return  object /NULL:PHP8未満のため実行できない.
  24: */
  25: function __construct($contents) {
  26:     //PHPのバージョンをチェックする.
  27:     if (! $this->isphp8over()) {
  28:         $this->error = 111;
  29:         throw new Exception($this->getErrorMessage());
  30:         return NULL;
  31:     }
  32:     //プロパティの初期値を設定する.
  33:     try {
  34:         $this->dom = new DOMDocument();
  35:         libxml_use_internal_errors(TRUE);
  36:         @$this->dom->loadHTML($contents);
  37:         libxml_clear_errors();
  38:         $this->xpath = new DOMXPath($this->dom);
  39:     } catch (Exception $e) {
  40:         $this->error = 121;
  41:         throw new Exception($e->getMessage());
  42:         return FALSE;
  43:     }
  44: 
  45:     //エラー番号 => エラー・メッセージ 一覧
  46:     $this->errorTable = array(
  47:         111 => array('PHP8以上が必要です', 'PHP8 or higher is required'),
  48:         121 => array('DOMエラーです', 'DOM error'),
  49:         999 => array('不明のエラー', 'unknown error'),
  50:     );
  51: }

PHPでクラスを使ってテキストの読みやすさを調べる」で簡単なクラスの作り方を紹介したが、今回は、スクレイピングを行うユーザー定義クラス pahooScraping を用意し、その中で最初に呼ばれるメソッド「コンストラクタ」を使って DOMDocumentクラス を用意することにする。
まず、pahooScrapingg クラス内で使用するプロパティ $dom, $xpath, $error を宣言する。

コンストラクタは、__construct によって定義する。引数は、クラスを呼び出した時の引数である。ここでは解析対象のテキストを引数とする。戻り値はない。

次に DOMDocumentクラスの使い方だが、その前に DOM について簡単に説明しておこう。
DOM とは Document Object Model の略で HTMLXML 文書をオブジェクトとして扱えるようにしたモデルのことだ。モデルというと分かりにくいのだが、要するに、HTMLやXMLをツリー構造(階層構造)のデータとして扱うことができるようになり、タグや属性名を指定して、目的のコンテンツを読み込んだり編集したりする手段を提供してくれる。
たとえば、<title> タグの内容を取得したり、<a> タグで指し示すリンクを全て取得する、といった処理が簡単にできるようになる。スクレイピングするのに、打って付けの仕組みだ。

コンストラクタで DOMDocument インスタンスを生成したら、DOMDocument::loadHTML を使って文字列からHTMLを読み込む。
DOM のエラー(例外)発生時に対応して、例外を返すようにしている。

ここで、文字コードに注意しなければならない。
DOMDocument::loadHTML は、HTML文書に記載されている文字コードセットを解釈しないようで、UTF-8などの日本語文字が文字化けを起こす。そこで、DOMDocument::loadHTML に渡す前に、関数  mb_convert_encoding  を使って HTML-ENTITIES にエンコードしてやる。こうすることで、日本語文字などは16進数に変換されるので、文字化けを起こすことはなくなる。

  53: /**
  54:  * デストラクタ
  55:  * @return  なし
  56: */
  57: function __destruct() {
  58:     unset($this->dom);
  59:     unset($this->xpath);
  60: }

クラスを解放する時にコールされるデストラクタを、念のために用意しておく。デストラクタは __destruct によって定義する。

解説:XPath式の使い方

 129: /**
 130:  * 指定したXPath式を評価し,結果を返す.
 131:  * @param   string $e XPath式
 132:  * @return  DOMNodeList/FALSE:評価失敗
 133: */
 134: function evalXPath($e) {
 135:     return $this->error ? FALSE : $this->xpath->evaluate("string({$e})");
 136: }

 147: /**
 148:  * titleを取得
 149:  * @return  titleの内容
 150: */
 151: function getTitle() {
 152:     return $this->evalXPath('/html/head/title');
 153: }

 155: /**
 156:  * descriptionを取得
 157:  * @return  descriptionの内容
 158: */
 159: function getDescription() {
 160:     return $this->evalXPath('/html/head/meta[@name="description"]/@content');
 161: }

読み込んだHTML文書を検索する方法はいくつかあるが、ここでは、ノードから検索する XPath式を用いることにする。
PHPでは、DOMXPath クラスを使う。コンストラクタで、DOMXPath でオブジェクトを生成するところまでは実行している。
次に、DOMXPath::evaluate を使ってXPath式の評価(検索)をしていく。そのためのメソッド evalXPath を用意した。

titleタグの内容を取り出すメソッド getTitle と、'meta name="description" content=' の内容を取り出すメソッド getDescription を用意した。

XPath式の書き方だが、'/' は階層を示す。
titleタグは 'html→head' の階層にあるので、'/html/head/title' と表記する。これにDOMXPath::evaluateを適用すると、titleタグの内容を取得できる。
'meta name="description" content=' をXPath式で表すと、'//meta[@name="description"]/@content' となる。XPath式で、条件は [...] に記載する。属性からノードを選択する場合には、名前の前に '@' を付ける。

XPath式の書き方は「XPath」(システムエンジニアのスキルアップ)が詳しい。

参考サイト

(この項おわり)
header