SimpleXmlとSimpleTag比較:twitterのhomeをスクレイピングしてみた。
事例としては、
<div id="content">
なタグを抽出とか
<div class="hentry">
なタグを全部抽出について比較します。
id は1つ、class は 複数 という違いがあるので、そのあたりも真面目に考えてみました。
なお、class 抽出のxpath式については CSSのセレクタをXPathに変換する 2007-02-05 - nazonoDiary からもらいました。
書き方
こんな感じで書けます。
idで抽出するとき、以下が等価になります。
SimpleXML:$xml->xpath('//div[@id="content"]') SimpleTag:getInAndParameter($tag, 'div', 'id', 'content');
classで抽出するとき、以下が等価になります。
SimpleXML:$xml->xpath('//tr[contains(concat(" ",@class," ")," hentry ")]') SimpleTag:getInAndParameter($tag, 'tr', 'class', 'hentry');
twitterからタイムラインを抜いてみる。
例として http://twitter.com/goungoun をスクレイピングしてタイムラインを抽出します。
SimpleXml で抽出するなら。
$xml = html_to_simplexml($html, false); $timelines = array(); $timelines = array_merge($timelines, $xml->xpath('//div[@id="content"]//div[contains(concat(" ",@class," ")," hentry ")]')); $timelines = array_merge($timelines, $xml->xpath('//table[@id="timeline"]//tr[contains(concat(" ",@class," ")," hentry ")]'));
SimpleTag で抽出するなら。
SimpleTag::setof($tag, $html, 'body'); $tag_div_content = getInAndParameter($tag, 'div', 'id', 'content'); $tag_table_timeline = getInAndParameter($tag, 'table', 'id', 'timeline'); $timelines = array(); $timelines = array_merge($timelines, getInAndParameter($tag_div_content, 'div', 'class', 'hentry')); $timelines = array_merge($timelines, getInAndParameter($tag_table_timeline, 'tr', 'class', 'hentry'));
実際に作ったもの。緑のバーをクリックすると見れます。
http://goungoun.dip.jp/app/open/rhaco-sample/simpletag003.php?name=goungoun
SimpleTag、getInAndParameter関数の実装
- getInしてforeachで回してます。
- id の時は、ヒットしても1タグなので、それを条件に処理を抜けるようにしてます。
- それ以外のときは、再帰で全てのタグを抽出してます。
- 再帰するか?正規表現使うか?も指定できます。
<?php /** * タグ名 が $tagName で パラメータ名 が $parameterId で パラメータ値 が $value のタグを全て抽出する。 * * @param SimpleTag $tag 配列でも可 * @param string $tagName 抽出条件 タグ名 * @param string $parameterId 抽出条件 パラメータ名 * @param string $value 抽出条件 パラメータ値 * @param string $recursive 再帰抽出するか? * @param string $value_rex パラメータ値を正規表現で評価するか? * @return array SimpleTagの配列 */ function getInAndParameter($tag, $tagName, $parameterId, $value, $recursive=null, $value_rex=null) { if ($recursive === null) { $recursive = ($parameterId === 'id' ? false : true); } if ($value_rex === null) { $value_rex = false; if ($parameterId === 'class') { $value_rex = true; $value = '/(^|\s)' . $value . '($|\s)/'; } } if (is_array($tag)) { $ret = array(); foreach ($tag as $item) { $ret = array_merge($ret, getInAndParameter($item, $tagName, $parameterId, $value, $recursive, $value_rex)); if (!$recursive && count($ret) === 1) break; } return $ret; } return _getInAndParameter($tag, $tagName, $parameterId, $value, $recursive, $value_rex); } function _getInAndParameter($tag, $tagName, $parameterId, $value, $recursive, $value_rex) { $ret = array(); foreach ($tag->getIn($tagName) as $item) { if ( (!$value_rex && $item->getParameter($parameterId) === $value) || ($value_rex && preg_match($value, $item->getParameter($parameterId)))) { $ret[] = $item; if (!$recursive) return $ret; } $ret = array_merge($ret, _getInAndParameter($item, $tagName, $parameterId, $value, $recursive, $value_rex)); if (!$recursive && count($ret) === 1) return $ret; } return $ret; }