ひょんなことからとあるサイトの全ページを解析して、
特定の文字列があるかどうかを調べることになった。
しかも、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は便利だ。