php-tool-box:url-collector
サイト内のURLを全て集めるPHP製クローラー - URL Collector
htmlSQLを使って、サイト内(同一ドメイン内)でリンクされているURLを片っ端から蒐集するクローラーを作ってみた。
これをエンジンとして、サイト内の新規Webページを調べてRSSで配信するPHP製フィード生成ツール - Feed MeisterとURLの配列を渡すとXML Sitemapを生成するPHP製ツール - Sitemapperも作った。
ライセンス
MITライセンスで。
使用例
<?php require('htmlsql/snoopy.class.php'); require('htmlsql/htmlsql.class.php'); $c = new UrlCollector(); $c->echoFlg = true; $arr = $c->getUrls('http://example.com/', 'タイトルに共通の部分'); $fp = fopen('test.log', 'a'); foreach ($arr as $url => $title) { fwrite($fp, "$url :\t$title\n"); } fclose($fp);
ソースコード
<?php /** * URL Collector * @see http://0-oo.net/sbox/php-tool-box/url-collector * @version 0.1.0 * @copyright 2010 dgbadmin@gmail.com * @license http://0-oo.net/pryn/MIT_license.txt (The MIT license) */ class UrlCollector { public $echoFlg; private $_wsql; private $_interval; private $_site; private $_len; private $_domain; private $_trim; private $_from; private $_to; private $_arr; /** * コンストラクタ * @param decimal $interval (省略可)次のページをクロールするまでの間隔(秒) */ public function __construct($interval = 1) { $this->echoFlg = false; $this->_wsql = new htmlsql(); $this->_interval = $interval * 1000 * 1000; //マイクロ秒にする } /** * URLを蒐集する * @param string $topUrl トップページのURL(この階層以下が蒐集対象になる) * @param string $titleTrim (省略可)タイトルから除外する文字列 * @param string $encoding (省略可)サイトの文字コード * @return array URL => ページタイトル */ public function getUrls($topUrl, $titleTrim = '', $encoding = 'UTF-8') { $urlArr = explode('/', $topUrl, -1); $siteUrl = implode('/', $urlArr) . '/'; $siteArr = parse_url($siteUrl); $this->_site = $siteUrl; $this->_len = strlen($siteUrl); $this->_domain = 'http://' . $siteArr['host']; $this->_trim = $titleTrim; $this->_from = $encoding; $this->_to = mb_internal_encoding(); $this->_arr = array(); $this->_dive($topUrl); //再帰処理 ksort($this->_arr); return $this->_arr; } private function _dive($url) { if ($this->echoFlg) { echo "$url\n"; } $wsql = $this->_wsql; //ページ取得 if (!$wsql->connect('url', $url) || $wsql->snoopy->status != 200) { return false; } if ($this->_from != $this->_to) { //文字コードが違う場合 $wsql->page = mb_convert_encoding($wsql->page, $this->_to, $this->_from); } //タイトルを取り出す if (!$wsql->query('SELECT text FROM title')){ return false; } $titles = $wsql->fetch_array(); $title = $titles[0]['text']; if ($this->_trim) { $title = mb_ereg_replace($this->_trim, '', $title); } $this->_arr[$url] = $title; //リンクを全て取り出す if (!$wsql->query('SELECT href FROM a')){ return false; } foreach($wsql->fetch_array() as $row){ $nextUrl = $this->_href2url($url, $row['href']); if ($nextUrl && !array_key_exists($nextUrl, $this->_arr)) { usleep($this->_interval); //ちょっと待つ if (!$this->_dive($nextUrl)) { $this->_arr[$nextUrl] = 'ERROR'; } } } return true; } private function _href2url($baseUrl, $href) { if (strpos($href, '#') !== false) { //ページ内リンクは無視 return false; } $href = htmlspecialchars_decode($href, ENT_QUOTES); switch (substr($href, 0, 1)) { //先頭の1文字で判断 case '?': //同一ページへのクエリー付きURLの場合 $baseArr = explode('?', $baseUrl); return $baseArr[0] . $href; case '/': //ドメイン内の絶対パスの場合 $url = $this->_domain . $href; break; default; if (strpos($href, ':')) { //スキーム指定有りの場合 $url = preg_replace('/:80/', '', $href); //ポート番号を削除 } else { //相対パスの場合 $url = $this->_getAbsoluteUrl($baseUrl, $href); //絶対パス化 } } if (substr($url, 0, $this->_len) != $this->_site) { return false; //他サイトやhttpsのリンクは追わない } return $url; } private function _getAbsoluteUrl($baseUrl, $href) { $baseArr = explode('/', $baseUrl); array_pop($baseArr); //現在のページを削除 $hrefArr = explode('/', $href); if ($hrefArr[0] == '.') { array_shift($hrefArr); } foreach ($hrefArr as $dir) { if ($dir != '..') { break; } array_pop($baseArr); array_shift($hrefArr); } $concatArr = array_merge($baseArr, $hrefArr); return implode('/', $concatArr); } }
php-tool-box/url-collector.txt · 最終更新: 2010/02/07 23:04 by dgbadmin