1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="keywords" content="php,tidy,スクレイピング,API,Webサービス" />
    <meta name="description" content="phpによるスクレイピング処理入門" />
    <title>phpによるスクレイピング処理入門:はてなfotolifeの情報を取得</title>
    <link href="style.css" rel="stylesheet" type="text/css">
    </head>
    <body>
    <?php
    define(INIT_URL , "http://f.hatena.ne.jp/");
    // 10件ごとに改行する
    define(PER_PAGE , 10);
    // IMGタグを探す際のキー
    define(CHECK_IMG_MARK ,"/images/fotolife/");
    // ページ取得時の「< prev」マークを調べる
    define(CHECK_PREV_MARK , "prev");
    // ページ取得時の「next >」マークを調べる
    define(CHECK_NEXT_MARK , "&gt;");
    // userIDを探す際URLを「/」で区切った際の5番目(添え字で4)がuserIDの場所
    //<img src="/images/fotolife/e/[username]/20070703/20070703104539_m.jpg" alt="egclab&#39;s fotolife">
    define(SEARCH_USER_POS , 4);
    // データを取得するURLはページによって変わる。
    // GETで渡された id を調べる
    // $_GET['id']がNULLでなければ
    if($_GET['id']<>""){
        // 各ページには50枚づつ写真があるので、50を掛けるが、
        // 実際のページ数との関係は (ページ-1)×50なので、1を引く
        $id = $_GET['id'] - 1;
        $idc = $id * 50;
        // urlはhttp://f.hatena.ne.jp/userlist?of=100
        // という形式なので、下記のように設定する。
        $targetURL = INIT_URL."userlist?of=".$idc;
    }else{
        $id = 0;
        // GETメソッドで何も渡されていなければ
        // 下記のようにセット
        $targetURL = INIT_URL."userlist";
    }
    // よく利用する関数をインクルード
    // get_html() 関数 / print_d() 関数
    // proxy利用時には、下記のファイルのproxy情報を書き換えてください
    include('hatena_fotolife_init.php');
    // proxy.phpをインクルード
    include('proxy.php');
    
    // tidyオブジェクトから画像情報を取得する
    function dump_nodes_fotolife(tidyNode $node, &$urls = NULL) {
        $urls = (is_array($urls)) ? $urls : array();
        if(isset($node->id)) {
        // ここからが実際のスクレイピング処理
            // $node->id が IMGタグだったら
            if($node->id == TIDY_TAG_IMG){
                // IMGタグのsrc属性が CHECK_IMG_MARK( = /images/fotolife/ )
                // を含んでいるか
                $pos = strpos ( $node->attribute['src'] ,CHECK_IMG_MARK);
                // IMGタグのsrc属性が /images/fotolife/ を含んでいれば
                if ($pos === 0){
                    // src 属性を 「/」で区切って配列$tmpに格納
                    $tmp = explode("/",$node->attribute['src']);
                    // useridは5番目の部分なので、$tmp[4]となる
                    // defineされている。
                    $userid = $tmp[SEARCH_USER_POS];
                    // img属性は相対パスで記載されているので、
                    // http://f.hatena.ne.jp/ を前にくっつける
                    $img = INIT_URL.$node->attribute['src'];
                    // user id と imgのパスを配列に格納する。
                    $urls[] = array("user" => $userid ,"img" =>$img);
                }
            }
        // ここまでが実際のスクレイピング処理
        }
        // 更に子供のノードが存在すれば、再帰的にdump_nodes_title()を繰り返す
        if($node->hasChildren()) {
            foreach($node->child as $c) {
                dump_nodes_fotolife($c, $urls);
            }
        }
        // 連想配列をリターン
        return $urls;
    }
    
    // tidyオブジェクトからページ情報を取得する
    function dump_nodes_page(tidyNode $node, &$page = NULL) {
        $page = (is_array($page)) ? $page : array();
        if(isset($node->id)) {
            // $node->id が Aタグだったら
            if($node->id == TIDY_TAG_A){
                // href属性が./userlist?of= を含んでいるか
                $pos = strpos ( $node->attribute['href'] ,"./userlist?of=");
                // href属性が./userlist?of= を含んでいたら
                if ($pos === 0){
                    // $node は <a href="./userlist?of=150">4</a>
                    // のようになっているので
                    // page_id を 4 の部分 $node->child[0]->value
                    // として、配列に格納する。
                    // 終端文字列を排除するためrtrimを利用
                    $page[] = rtrim ($node->child[0]->value);
                }
            }
        }
        // 更に子供のノードが存在すれば、再帰的にdump_nodes_page()を繰り返す
        if($node->hasChildren()) {
            foreach($node->child as $c) {
                dump_nodes_page($c, $page);
            }
        }
        // 連想配列をリターン
        return $page;
    }
    // ページナビゲーション表示用
    function print_navi($page){
        $pager_head = "";
        $pager_foot = "";
        $pager_body = "";
        // $page内の配列の数だけ繰り返し
        foreach($page as $item){
            switch ($item){
            // prevの場合
            case ( strpos($item , CHECK_PREV_MARK) > 0 ) :
                $pager_head = '<a href="'.$SCRIPT_NAME.'?id='.($_GET['id'] - 1).'">&lt;prev </a>';
            break;
            // nextの場合
            case ( strpos($item , CHECK_NEXT_MARK) > 0) :
                $pager_foot = '<a href="'.$SCRIPT_NAME.'?id='.($_GET['id'] + 1).'">next&gt;</a>';
            break;
            // それ以外
            default:
                $pager_body .= '<a href="'.$SCRIPT_NAME.'?id='.$item.'">'.$item.'</a> ';
            }
        }
        // ページナビゲーションを表示
        echo $pager_head.$pager_body.$pager_foot;
    }
    // ここからメイン関数
    // このページへのリンク
    echo '<a href="'.$SCRIPT_NAME.'">TOP</a><br />';
    // 取得対象のURLを表示してみる。
    echo '<a href="'.$targetURL.'" target="_blank">'.$targetURL.'</a><br />';
    // targetURLで指定されたHTMLファイルを取得して$dataに格納
    // init.phpにget_html()関数が入っています。
    $data = get_html($targetURL);
    // Tidy関数で整形する際のおまじない。
    // 取得したHTMLファイルの内容をUTF-8の文字コードで$configで
    // 指定した設定で、tidyオブジェクトを作成
    // $config は hatena_fotolife_init.php で定義
    $tidy = tidy_parse_string($data, $config, 'UTF8');
    // cleanRepairで整形
    $tidy->cleanRepair();
    // 作成したtidyオブジェクトのbody要素の内容をdump_nodes_fotolife()に渡す
    // $urlsにはuser,imgのペアの配列が格納されて返ってくる。
    $urls = dump_nodes_fotolife($tidy->body());
    // ページ情報を取得するために、dump_nodes_page()関数を呼び出す
    // $page には、page情報が格納されている
    $page = dump_nodes_page($tidy->body());
    // 現在のページ( $id + 1 )を配列に格納
    array_push($page , $id+1);
    // 数値としてソート
    sort($page,SORT_NUMERIC);
    // ページ情報の配列をprint_navi()関数に渡す
    print_navi($page);
    ?>
    <div align="center">
    <?php
    $count = 0;
    // 取得した写真を表示する
    // $urlsの数だけ foreach する
    // ホントはテーブルを使って表示するとよいと思われます。
    foreach ($urls as $item){
        // 取得した情報を表示
        // リンク先 : http://f.hatena.ne.jp/ + $item['user']
        // 画像ファイルパス : $item['img']
        echo '<a href="'.INIT_URL.$item['user'].'/" target="_blank"><img src="'.$item['img'].'" border="0" alt="'.$item['user'].'さん" /></a>';
        if (($count+1) % PER_PAGE ==0) echo '<br clear="all" />';
        $count++;
    }
    ?>
    </div>
    </body>
    </html>