<!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 , ">");
- // userIDを探す際URLを「/」で区切った際の5番目(添え字で4)がuserIDの場所
- //<img src="/images/fotolife/e/[username]/20070703/20070703104539_m.jpg" alt="egclab'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).'"><prev </a>';
- break;
- // nextの場合
- case ( strpos($item , CHECK_NEXT_MARK) > 0) :
- $pager_foot = '<a href="'.$SCRIPT_NAME.'?id='.($_GET['id'] + 1).'">next></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>
-