ゼロと無限の間に

フリーでオープンソースなJavaScriptとかPHPとか。

ユーザ用ツール

サイト用ツール


php-tool-box:url-collector

サイト内のURLを全て集めるPHP製クローラー - URL Collector

htmlSQLを使って、サイト内(同一ドメイン内)でリンクされているURLを片っ端から蒐集するクローラーを作ってみた。

これをエンジンとして、サイト内の新規Webページを調べてRSSで配信するPHP製フィード生成ツール - Feed MeisterURLの配列を渡すとXML Sitemapを生成するPHP製ツール - Sitemapperも作った。

ライセンス

使用例

<?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

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki