ふぁぼったーをパースしてみたよ
ふぁぼったーにはAPIもないっぽいし、パースするライブラリが単体で配布されてなかったので、PHP Simple HTML DOM Parserの練習ついでに作ってみました。
くれぐれもふぁぼったーの鯖に負荷をかけるような使い方はしないよう、お願いします。*1
動作サンプル
利用するためには、PHP Simple HTML DOM Parserのsimple_html_dom.phpが必要です。
同じディレクトリか、パスが通っている場所に置いといてください。
PHP Simple HTML DOM ParserはjQueryと同じ感覚で使えるので、パースする際にはかなり便利です。
<?php /***************************favotter_lib.php**************************** Author: shak (http://d.hatena.ne.jp/shak/) Version: 0.1 Licensed under The MIT License Requires simple_html_dom.php (http://simplehtmldom.sourceforge.net/) ***********************************************************************/ /* EXAMPLE */ /* require_once('favotter_lib.php'); $favotter_lib = favotterLib(); $option = array( 'screen_name' => 'screen_name', 'mode' => 'new', 'threshold' => 1, 'limit' => 40, 'page' => 1, 'data_type' => 0, ); $data = $favotter_lib->get($option); if ($data) print_r($option); else print_r($favotter_lib->notice); */ class favotterLib { var $screen_name = null; //(string)取得対象となるスクリーンネーム var $mode = null; //(string)モード:home.phpではnew/best、userではnew/best/fav var $threshold = 1; //(int)favしきい値:phpだしstringでも構わない。 var $limit = 100; //(int)data取得件数。多めに取得しちゃったら切り捨て。 var $page = 1; //(int)開始ページ数:1始まり。thresholdと同様stringでも構わない気がする。 var $notice; //(array)最後に実行した関数の実行ログ:最後の要素の行頭が'$'ならFavotter落ちの可能性、'%'ならデータ終端の可能性、'!'ならその他エラー。{end($notice); echo current($notice);}など。 var $favorited = 0; //(string)ふぁぼられ数:user()を実行した際に自動で取得。一応取得時点ではstring。 var $data; //(array)ふぁぼられpost:連想配列→配列の入れ子。連想配列のkeyはtext,screen_name,updated,twitter_link,favotter_link,favotters(favした人のscreen_nameを配列で格納),favorited(被fav数)。 var $data_type = 0; //0なら$dataの形式は上記の通りだけど、1なら配列→連想配列の入れ子になる。 function __construct( $screen_name=null ) { require_once 'simple_html_dom.php'; $this->notice = array(); if ($this->data_type) { $this->data = array(); } else { $this->data = array( 'text' => array(), 'screen_name' => array(), 'updated' => array(), 'favotter_link' => array(), 'twitter_link' => array(), 'favotters' => array(), 'favorited' => array(), ); } if (isset($screen_name)) { $this->screen_name = $screen_name; } } //screen_nameがあればuser()、なければhome()を実行する。 function get( $option ) { if (empty($this->screen_name) && !isset($option['screen_name'])) return $this->home($option); else return $this->user($option); } //ユーザー毎のふぁぼられを取得。 //screen_nameは何らかの方法でセットされないとエラー。mode=new,threshold=1,page=1がfavotter側のデフォ。 function user( $option ) { $this->favorited = 0; if (isset($option['data_type'])) $this->data_type = $option['data_type']; $this->__construct(); //$optionを配列に格納したりargsに設定したり。 //$optionで設定しなくても、直接$favotterLib->modeとかに代入しても良い。 $names = array('screen_name', 'limit', 'mode', 'threshold', 'page'); for ($i=0; $i<count($names); $i++) { if (isset($option[$names[$i]])) { $this->notice[] = sprintf('set %s to %s.', $names[$i], $option[$names[$i]]); $this->{$names[$i]} = $option[$names[$i]]; } else if (empty($this->{$names[$i]})) { if ($names[$i] == 'screen_name') { //ユーザー名が空ならエラー吐いて終了。 $this->notice[] = '![screen_name] is empty.'; return false; } $this->notice[] = sprintf('[%s] is empty.', $names[$i]); } else { $this->notice[] = sprintf('use %s=%s asis.', $names[$i], $this->{$names[$i]}); } } //引数配列生成(あとで結合) $args = array(); for ($i=2; $i<count($names); $i++) { if (!empty($this->{$names[$i]})) { $args[] = sprintf('%s=%s', $names[$i], $this->{$names[$i]}); } } if (isset($option['limit'])) $this->limit = $option['limit']; $current_page = $this->page; for ($j=0; $j<$this->limit/20; $j++) { //html取得 $html = new simple_html_dom(); $url = sprintf('http://favotter.net/user/%s?%s', $this->screen_name, implode('&', $args)); //$argsが空配列ならimplodeはnullを返すから問題ないはず $html->load_file($url); //Favotterダウン時処理 if (strpos($html->plaintext, 'Sorry, Database Server is down なう...') !== false || empty($html)) { $this->notice[] = '$favotter seems to be down now.'; return false; } //ふぁぼられ数取得 //結構大雑把な正規表現 $desc = $html->find('div.message-left div.description', 0); if ( preg_match('/a\>のふぁぼられ\((\d+)?\)\<d/', $desc->outertext, $match) ) { $this->favorited = $match[1]; } //ふぁぼられpost群取得 $bubbles = $html->find('.bubble'); if (count($bubbles) == 0) break; $info = $html->find('.info'); if ($this->data_type) $start = count($this->data); else $start = count($this->data['text']); for ($i=0; $i<count($bubbles); $i++) { if ($this->data_type) { $this->data[$i+$start] = array( 'text' => trim($bubbles[$i]->find('span', 0)->plaintext), 'screen_name' => $info[$i]->find('strong', 0)->find('a', 0)->title, 'favotter_link' => 'http://favotter.net'.$info[$i]->find('a', 1)->href, 'updated' => date('Y-m-d H:i:s', strtotime($info[$i]->find('a', 1)->find('abbr', 0)->title)), 'twitter_link' => $info[$i]->find('a', 2)->href, 'favotters' => array(), ); foreach ( $info[$i]->find('span', 1)->find('a') as $frst ) { $this->data[$i+$start]['favotters'][] = $frst->find('img', 0)->title; } $this->data[$i+$start]['favorited'] = count($this->data[$i+$start]['favotters']); if (count($this->data) >= $this->limit) break 2; } else { $this->data['text'][$i+$start] = trim($bubbles[$i]->find('span', 0)->plaintext); $this->data['screen_name'][$i+$start] = $info[$i]->find('strong', 0)->find('a', 0)->title; $this->data['favotter_link'][$i+$start] = 'http://favotter.net'.$info[$i]->find('a', 1)->href; $this->data['updated'][$i+$start] = date('Y-m-d H:i:s', strtotime($info[$i]->find('a', 1)->find('abbr', 0)->title)); $this->data['twitter_link'][$i+$start] = $info[$i]->find('a', 2)->href; $this->data['favotters'][$i+$start] = array(); foreach ( $info[$i]->find('span', 1)->find('a') as $frst ) { $this->data['favotters'][$i+$start][] = $frst->find('img', 0)->title; } $this->data['favorited'][$i+$start] = count($this->data['favotters'][$i+$start]); if (count($this->data['text']) >= $this->limit) break 2; } } $current_page++; $args['page'] = 'page='.$current_page; $html->clear(); } if (empty($this->data)) { $this->notice[] = '%no data found.'; return false; } return $this->data; } function home( $option ) { //基本的にuser()と同じルーチンだけど細かいところが違う。([screen_name]周りとか) if (isset($option['data_type'])) $this->data_type = $option['data_type']; $this->__construct(); //$optionを配列に格納したりargsに設定したり。 //$optionで設定しなくても、直接$favotterLib->modeとかに代入しても良い。 $names = array('limit', 'mode', 'threshold', 'page'); for ($i=0; $i<count($names); $i++) { if (isset($option[$names[$i]])) { $this->notice[] = sprintf('set %s to %s.', $names[$i], $option[$names[$i]]); $this->{$names[$i]} = $option[$names[$i]]; } else if (empty($this->{$names[$i]})) { $this->notice[] = sprintf('[%s] is empty.', $names[$i]); } else { $this->notice[] = sprintf('use %s=%s asis.', $names[$i], $this->{$names[$i]}); } } //引数配列生成(あとで結合) $args = array(); for ($i=1; $i<count($names); $i++) { if (!empty($this->{$names[$i]})) { $args[] = sprintf('%s=%s', $names[$i], $this->{$names[$i]}); } } if (isset($option['limit'])) $this->limit = $option['limit']; $current_page = $this->page; for ($j=0; $j<$this->limit/50; $j++) { //html取得 $html = new simple_html_dom(); $url = sprintf('http://favotter.net/home.php?%s', implode('&', $args)); //$argsが空配列ならimplodeはnullを返すから問題ないはず $html->load_file($url); //Favotterダウン時処理 if (strpos($html->plaintext, 'Sorry, Database Server is down なう...') !== false || empty($html)) { $this->notice[] = '$favotter seems to be down now.'; return false; } //ふぁぼられ数取得 $desc = $html->find('div.message-left div.description', 0); if ( preg_match('/a\>のふぁぼられ\((\d+)?\)\<d/', $desc->outertext, $match) ) { $this->favorited = $match[1]; } //ふぁぼられpost群取得 $bubbles = $html->find('.bubble'); if (count($bubbles) == 0) break; $info = $html->find('.info'); if ($this->data_type) $start = count($this->data); else $start = count($this->data['text']); for ($i=0; $i<count($bubbles); $i++) { if ($this->data_type) { $this->data[$i+$start] = array( 'text' => trim($bubbles[$i]->find('span', 0)->plaintext), 'screen_name' => $info[$i]->find('strong', 0)->find('a', 0)->title, 'favotter_link' => 'http://favotter.net'.$info[$i]->find('a', 1)->href, 'updated' => date('Y-m-d H:i:s', strtotime($info[$i]->find('a', 1)->find('abbr', 0)->title)), 'twitter_link' => $info[$i]->find('a', 2)->href, 'favotters' => array(), ); foreach ( $info[$i]->find('span', 1)->find('a') as $frst ) { $this->data[$i+$start]['favotters'][] = $frst->find('img', 0)->title; } $this->data[$i+$start]['favorited'] = count($this->data[$i+$start]['favotters']); if (count($this->data) >= $this->limit) break 2; } else { $this->data['text'][$i+$start] = trim($bubbles[$i]->find('span', 0)->plaintext); $this->data['screen_name'][$i+$start] = $info[$i]->find('strong', 0)->find('a', 0)->title; $this->data['favotter_link'][$i+$start] = 'http://favotter.net'.$info[$i]->find('a', 1)->href; $this->data['updated'][$i+$start] = date('Y-m-d H:i:s', strtotime($info[$i]->find('a', 1)->find('abbr', 0)->title)); $this->data['twitter_link'][$i+$start] = $info[$i]->find('a', 2)->href; $this->data['favotters'][$i+$start] = array(); foreach ( $info[$i]->find('span', 1)->find('a') as $frst ) { $this->data['favotters'][$i+$start][] = $frst->find('img', 0)->title; } $this->data['favorited'][$i+$start] = count($this->data['favotters'][$i+$start]); if (count($this->data['text']) >= $this->limit) break 2; } } $current_page++; $args['page'] = 'page='.$current_page; $html->clear(); } if (empty($this->data)) { $this->notice[] = '%no data found.'; return false; } return $this->data; } } ?>