1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  5. <meta name="keywords" content="php,tidy,スクレイピング,API,Webサービス" />
  6. <meta name="description" content="phpによるスクレイピング処理入門" />
  7. <title>phpによるスクレイピング処理入門:はてなfotolifeの情報を取得</title>
  8. <link href="style.css" rel="stylesheet" type="text/css">
  9. </head>
  10. <body>
  11. <?php
  12. define(INIT_URL "http://f.hatena.ne.jp/");
  13. // 10件ごとに改行する
  14. define(PER_PAGE 10);
  15. // IMGタグを探す際のキー
  16. define(CHECK_IMG_MARK ,"/images/fotolife/");
  17. // ページ取得時の「< prev」マークを調べる
  18. define(CHECK_PREV_MARK "prev");
  19. // ページ取得時の「next >」マークを調べる
  20. define(CHECK_NEXT_MARK "&gt;");
  21. // userIDを探す際URLを「/」で区切った際の5番目(添え字で4)がuserIDの場所
  22. //<img src="/images/fotolife/e/[username]/20070703/20070703104539_m.jpg" alt="egclab&#39;s fotolife">
  23. define(SEARCH_USER_POS 4);
  24. // データを取得するURLはページによって変わる。
  25. // GETで渡された id を調べる
  26. // $_GET['id']がNULLでなければ
  27. if($_GET['id']<>""){
  28.     // 各ページには50枚づつ写真があるので、50を掛けるが、
  29.     // 実際のページ数との関係は (ページ-1)×50なので、1を引く
  30.     $id $_GET['id'] - 1;
  31.     $idc $id 50;
  32.     // urlはhttp://f.hatena.ne.jp/userlist?of=100
  33.     // という形式なので、下記のように設定する。
  34.     $targetURL INIT_URL."userlist?of=".$idc;
  35. }else{
  36.     $id 0;
  37.     // GETメソッドで何も渡されていなければ
  38.     // 下記のようにセット
  39.     $targetURL INIT_URL."userlist";
  40. }
  41. // よく利用する関数をインクルード
  42. // get_html() 関数 / print_d() 関数
  43. // proxy利用時には、下記のファイルのproxy情報を書き換えてください
  44. include('hatena_fotolife_init.php');
  45. // proxy.phpをインクルード
  46. include('proxy.php');
  47. // tidyオブジェクトから画像情報を取得する
  48. function dump_nodes_fotolife(tidyNode $node, &$urls NULL) {
  49.     $urls = (is_array($urls)) ? $urls : array();
  50.     if(isset($node->id)) {
  51.     // ここからが実際のスクレイピング処理
  52.         // $node->id が IMGタグだったら
  53.         if($node->id == TIDY_TAG_IMG){
  54.             // IMGタグのsrc属性が CHECK_IMG_MARK( = /images/fotolife/ )
  55.             // を含んでいるか
  56.             $pos strpos $node->attribute['src'] ,CHECK_IMG_MARK);
  57.             // IMGタグのsrc属性が /images/fotolife/ を含んでいれば
  58.             if ($pos === 0){
  59.                 // src 属性を 「/」で区切って配列$tmpに格納
  60.                 $tmp explode("/",$node->attribute['src']);
  61.                 // useridは5番目の部分なので、$tmp[4]となる
  62.                 // defineされている。
  63.                 $userid $tmp[SEARCH_USER_POS];
  64.                 // img属性は相対パスで記載されているので、
  65.                 // http://f.hatena.ne.jp/ を前にくっつける
  66.                 $img INIT_URL.$node->attribute['src'];
  67.                 // user id と imgのパスを配列に格納する。
  68.                 $urls[] = array("user" => $userid ,"img" =>$img);
  69.             }
  70.         }
  71.     // ここまでが実際のスクレイピング処理
  72.     }
  73.     // 更に子供のノードが存在すれば、再帰的にdump_nodes_title()を繰り返す
  74.     if($node->hasChildren()) {
  75.         foreach($node->child as $c) {
  76.             dump_nodes_fotolife($c$urls);
  77.         }
  78.     }
  79.     // 連想配列をリターン
  80.     return $urls;
  81. }
  82. // tidyオブジェクトからページ情報を取得する
  83. function dump_nodes_page(tidyNode $node, &$page NULL) {
  84.     $page = (is_array($page)) ? $page : array();
  85.     if(isset($node->id)) {
  86.         // $node->id が Aタグだったら
  87.         if($node->id == TIDY_TAG_A){
  88.             // href属性が./userlist?of= を含んでいるか
  89.             $pos strpos $node->attribute['href'] ,"./userlist?of=");
  90.             // href属性が./userlist?of= を含んでいたら
  91.             if ($pos === 0){
  92.                 // $node は <a href="./userlist?of=150">4</a>
  93.                 // のようになっているので
  94.                 // page_id を 4 の部分 $node->child[0]->value
  95.                 // として、配列に格納する。
  96.                 // 終端文字列を排除するためrtrimを利用
  97.                 $page[] = rtrim ($node->child[0]->value);
  98.             }
  99.         }
  100.     }
  101.     // 更に子供のノードが存在すれば、再帰的にdump_nodes_page()を繰り返す
  102.     if($node->hasChildren()) {
  103.         foreach($node->child as $c) {
  104.             dump_nodes_page($c$page);
  105.         }
  106.     }
  107.     // 連想配列をリターン
  108.     return $page;
  109. }
  110. // ページナビゲーション表示用
  111. function print_navi($page){
  112.     $pager_head "";
  113.     $pager_foot "";
  114.     $pager_body "";
  115.     // $page内の配列の数だけ繰り返し
  116.     foreach($page as $item){
  117.         switch ($item){
  118.         // prevの場合
  119.         case ( strpos($item CHECK_PREV_MARK) > ) :
  120.             $pager_head '<a href="'.$SCRIPT_NAME.'?id='.($_GET['id'] - 1).'">&lt;prev </a>';
  121.         break;
  122.         // nextの場合
  123.         case ( strpos($item CHECK_NEXT_MARK) > 0) :
  124.             $pager_foot '<a href="'.$SCRIPT_NAME.'?id='.($_GET['id'] + 1).'">next&gt;</a>';
  125.         break;
  126.         // それ以外
  127.         default:
  128.             $pager_body .= '<a href="'.$SCRIPT_NAME.'?id='.$item.'">'.$item.'</a> ';
  129.         }
  130.     }
  131.     // ページナビゲーションを表示
  132.     echo $pager_head.$pager_body.$pager_foot;
  133. }
  134. // ここからメイン関数
  135. // このページへのリンク
  136. echo '<a href="'.$SCRIPT_NAME.'">TOP</a><br />';
  137. // 取得対象のURLを表示してみる。
  138. echo '<a href="'.$targetURL.'" target="_blank">'.$targetURL.'</a><br />';
  139. // targetURLで指定されたHTMLファイルを取得して$dataに格納
  140. // init.phpにget_html()関数が入っています。
  141. $data get_html($targetURL);
  142. // Tidy関数で整形する際のおまじない。
  143. // 取得したHTMLファイルの内容をUTF-8の文字コードで$configで
  144. // 指定した設定で、tidyオブジェクトを作成
  145. // $config は hatena_fotolife_init.php で定義
  146. $tidy tidy_parse_string($data$config'UTF8');
  147. // cleanRepairで整形
  148. $tidy->cleanRepair();
  149. // 作成したtidyオブジェクトのbody要素の内容をdump_nodes_fotolife()に渡す
  150. // $urlsにはuser,imgのペアの配列が格納されて返ってくる。
  151. $urls dump_nodes_fotolife($tidy->body());
  152. // ページ情報を取得するために、dump_nodes_page()関数を呼び出す
  153. // $page には、page情報が格納されている
  154. $page dump_nodes_page($tidy->body());
  155. // 現在のページ( $id + 1 )を配列に格納
  156. array_push($page $id+1);
  157. // 数値としてソート
  158. sort($page,SORT_NUMERIC);
  159. // ページ情報の配列をprint_navi()関数に渡す
  160. print_navi($page);
  161. ?>
  162. <div align="center">
  163. <?php
  164. $count 0;
  165. // 取得した写真を表示する
  166. // $urlsの数だけ foreach する
  167. // ホントはテーブルを使って表示するとよいと思われます。
  168. foreach ($urls as $item){
  169.     // 取得した情報を表示
  170.     // リンク先 : http://f.hatena.ne.jp/ + $item['user']
  171.     // 画像ファイルパス : $item['img']
  172.     echo '<a href="'.INIT_URL.$item['user'].'/" target="_blank"><img src="'.$item['img'].'" border="0" alt="'.$item['user'].'さん" /></a>';
  173.     if (($count+1) % PER_PAGE ==0) echo '<br clear="all" />';
  174.     $count++;
  175. }
  176. ?>
  177. </div>
  178. </body>
  179. </html>