ひょんなことからとあるサイトの全ページを解析して、

特定の文字列があるかどうかを調べることになった。


しかも、PHPで。


PHPにも、

HTMLを解析するWebスクレイピングライブラリがあるけど、

それを使わず、自作してみた。




始めに、

URLを指定したら、指定先からHTMLが返ってくる様にするにはどうすれば良いか?を調べていたら、

file_get_contents("指定先のURL");

で指定先のHTMLが返ってくることが分かった。


file_get_contentsは同一サーバ内のファイル名を指定した場合ではなく、

別サーバ間でもファイルを取得できるらしく、

これを使えば、全ページのHTMLを取得すること自体は簡単じゃね?なんて思ったので書いてみた。




作成したコードは下記の通り、


class Crawle{

	private $domain;
	private $urlList = array();
	private $regex = '@<a[^>]*?(?<!\.)href="([^"]*+)"[^>]*+>(.*?)@si';
	
	function execute(){
		$this->domain = "example.com";
		$url = "http://" . $this->domain;
		self::crawle($url);
	}

	/**
	 * 指定したサイトのページのHTMLを取得し、
	 * 取得したHTMLから同一ドメインのURLを取得し、
	 * 再帰的にcrawleを実行する
	 */
	private function crawle($url){
		$html = file_get_contents($url);

		//HTMLを取得できなければ終了
		if(!isset($html)) return;

		//HTMLの解析を行う。
		self::scrape($html);

		//次のページのURLを取得
		preg_match_all($this->regex, $html, $matches, PREG_SET_ORDER);
		
		//URLの記述が無ければ終了
		if(!count($matches)) return;

		foreach($matches as $match){
			//$match[1]にリンクに記載されていたURLが格納されている
			if(!isset($match[1]) continue;

			//同一ドメインでないものは弾く
			if(!strpos($match[1], $this->domain)) continue;

			//すでに解析したURLは弾く
			if(in_array($match[1], $this->urlList)) continue;

			//これから解析するページを配列に入れておく
			$this->urlList[] = $match[1];

			//解析を再帰的に実行
			self::crawle($match[1]);
		}
	}

	private function scrape($html){
		//HTML解析の際の値の取得に関する処理を書く
	}
}

とりあえず、$urlを突っ込んだら再帰的に実行してくれる関数を用意して、ページ内のリンクを片っ端から実行する様にすれば、全ページのHTMLの解析はできる様になると。


だけど、このコードだと、

・ページ数が多い場合、記録用の配列のサイズが大きくなりすぎる

という問題があるし、


次から次へと実行してしまうため、

HTMLを返すサイトのサーバの負荷がそれなりに掛ってしまうので、

そこらへんの修正は必要だなと。

※実際にはドメイン以下のURLのみを配列に入れて、比較を行いました。


あと、このコードを書いていて気が付いたけど、

/(スラッシュ)始まりのURLも取得できないな。


とりあえず分かったことは、

PHPのfile_get_contentsは便利だ。