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





