PHPではてブスクレイピング+α
- タグ検索結果からサイト名とURLを取得
- 被ブクマ数を取得
- XML形式で出力
というシンプルな内容です。
スクレイピングには、PHP凡庸スクレイピングライブラリを作ってみたで紹介されているライブラリを少々いじって利用。また、被ブクマ数取得にはAPIを使った。(参考サイト:はてなブックマーク件数取得API PHP版サンプル)
ソース:
HatebScraper.Class.php
<? class HatebScraper{ function __construct($url="", $encoding="", $noQuery=""){ (!empty($url)) ? $this->url = $url : $this->errors[] = "URLを設定してください"; switch (true) { case empty($encoding) : $this->errors[] = "URLの文字エンコーディングを設定してください"; break; case eregi("SHIFT(-|_)JIS", $encoding) : $this->encoding = "SJIS-win"; break; case eregi("EUC-JP", $encoding) : $this->encoding = "eucJP-win"; break; case eregi("UTF-8", $encoding) : $this->encoding = "UTF-8"; break; case eregi("ISO-2022-JP", $encoding) : $this->encoding = "ISO-2022-JP"; break; case eregi("JIS", $encoding) : $this->encoding = "JIS"; break; case eregi("ASCII", $encoding) : $this->encoding = "ASCII"; break; default : $this->errors[] = "不明なエンコーディングです"; } if (empty($noQuery)) { if (!empty($_GET["tag"])) { $keyword = mb_convert_encoding($_GET["tag"], $this->encoding, "UTF-8"); $this->keyword = rawurlencode($keyword); $this->url .= $this->keyword; } else { $this->errors[] = "検索キーワードを入力してください"; } if( !empty($_GET['page']) ){ $this->page = 25 * $_GET['page']; $this->url .= "&of=" . $this->page; } switch($_GET['sort']) { case 'hot' : case 'eid' : case 'count': $this->url .="&sort=" . $_GET['sort']; break; default : //do nothing } } } // HTMLを取得して、SimpleXMLObjectに変換 function retrieve(){ $html = $this->getHTML(); //SimpleXMLオブジェクトに変換 $this->xml = $this->html_to_simplexml($html); } // 要素を抽出 function pickUpElement($xpath=""){ (empty($xpath)) ? $this->errors[] = "対象の要素をXPath式で指定してください" : ""; // 配列をセット foreach ($this->xml->xpath($xpath) as $item){ $items[] = (string) str_replace("&", "&", $item->asXML()); } return $items; } // いったん、別のエンコードに変換したキーワードを再度UTF-8に変換し直す function convertKeyword(){ $keyword = rawurldecode($this->keyword); $keyword = mb_convert_encoding($keyword, "UTF-8", $this->encoding); return $keyword; } // HTMLを取得 function getHTML(){ $client = new HTTP_Client(); $client->setDefaultHeader(array('User-Agent' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')); $client->get($this->url); $result = $client->currentResponse(); switch($this->encoding){ case "SJIS-win" : $this->encoding_html = "shift_jis"; break; case "eucJP-win": $this->encoding_html = "euc-jp"; break; default : $this->encoding_html = $this->encoding; } $html = $result["body"]; $html = preg_replace("/".$this->encoding_html."/i", "utf-8", $html); $html = mb_convert_encoding($html, "UTF-8", $this->encoding); // コメントを削除 $html = preg_replace("/<!--([^-]|-[^-]|--[^>])*-->/", "", $html); $html = str_replace("&", "&", $html); if (preg_match("/<title>(.*)<\/title>/i", $html, $match)){ $this->title = $match[1]; } return $html; } // SimpleXMLオブジェクトに変換 function html_to_simplexml($html_){ // DOMDocumentの文字化け対策 $html_ = preg_replace('/<title>/i', '<meta http-equiv="content-type" content="text/html; charset=utf-8"><title>', $html_); @$dom = new DOMDocument("1.0", "utf-8"); @$dom->loadHTML($html_); // DOMをSimpleXMLへ変換 $ret = simplexml_import_dom($dom); $str = $ret->asXML(); // XML宣言付与 if (true !== preg_match('/^<\\?xml version="1.0"/', $str)) { $str = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $str; } $ret = simplexml_load_string($str); return $ret; } // エラーがあった場合、エラーメッセージを含む文字列を返す function isError(){ if (count($this->errors) > 0) { foreach($this->errors as $error) { $msg .= "<li>{$error}</li>\n"; } echo "<ul>". $msg ."</ul>"; } else { return false; } } } ?>
hateb.php
<?php //$_GETにtag, page, sortを指定可能 require_once "HTTP/Client.php"; require_once "XML/RPC.php"; require_once "HatebScraper.Class.php"; $scraper = new HatebScraper("http://b.hatena.ne.jp/t?tag=", "UTF-8"); if ( empty($_GET["tag"]) ) { echo "no tag"; exit; } // XMLに変換して格納(その際に不必要なタグを除去) $scraper->retrieve(); // 要素を抽出して、変数に代入 $titles = $scraper->pickUpElement('//a[@class="bookmark"]'); $urls = $scraper->pickUpElement('//a[@class="bookmark"]/@href'); $count = count($titles); //Get Hateb User $params = array(); for($i=0;$i<$count;$i++) { //strip href part $urls[$i] = strstr($urls[$i], '"'); $urls[$i] = substr($urls[$i], 1, (strlen($urls[$i])-2) ); $params[$i] = new XML_RPC_Value($urls[$i], 'string'); } $msg = new XML_RPC_Message('bookmark.getCount', $params); $cli = new XML_RPC_Client('/xmlrpc', 'b.hatena.ne.jp'); $resp = $cli->send($msg); if (!$resp) { echo 'Communication error: ' . $cli->errstr; exit; } if( !$resp->faultCode() ) { $data = XML_RPC_decode( $resp->value() ); } else { var_dump($resp->faultCode()); var_dump($resp->faultString()); exit; } $sites = array(); for($i=0;$i<$count;$i++) { $sites[$i] = array( 'title' => $titles[$i], 'url' => $urls[$i], 'user' => $data[$urls[$i]] ); } // キーワードをUTF-8に戻す $scraper->keyword = $scraper->convertKeyword(); // Output XML header("Content-Type: text/xml;charset=utf-8"); echo '<?xml version="1.0"?>'."\n"; echo "<results>"; if ($scraper->isError() !== false) { $scraper->isError(); } else { $msg = ''; for ($i=0; $i<$count; $i++){ $msg .= '<result id="'. ($i + 1) .'">'; $msg .= "<title>". strip_tags($sites[$i]['title'])."</title>"; $msg .= "<url>".strip_tags($sites[$i]['url'])."</url>"; $msg .= "<user>".strip_tags($sites[$i]['user'])."</user>"; $msg .= "</result>"; } echo html_entity_decode($msg); } echo "</results>"; ?>
hateb.php?tag=Tag&page=1&sort=count