PHPでスクレイピング

以前は、PHP Simple HTML DOM Parserってのを使っていましたが、久しぶりに調べてみると、phpQueryというのがあるようなので、それを使ってみる。
オリジナルのものはここからダウンロードできるようです。
が、しばらくメンテされてないようです。ForkしてComposerに対応したものもあるようです(試してません)。
なお、あまりTableを対象としたサンプルが無かったのでテーブルのスクレイピングを中心に書きます。
シンプルなデモ
このようなHTML(test.html)があるとすると、
<!doctype html>
<html>
<head>
<title>scraping test</title>
</head>
<body>
<h1>hoge</h1>
<!-- table1 -->
<div id="greeting">Hello</div>
<table>
<tr>
<td>name</td>
<td>hoge</td>
</tr>
<tr>
<td>email</td>
<td>hoge@hoge.com</td>
</tr>
</table>

<!-- table2 -->
<table>
<tr>
<td>address</td>
<td>tokyo</td>
</tr>
<tr>
<td>tel</td>
<td>03-1234-5678</td>
</tr>
<tr>
<td>image</td>
<td><img src="hoge.png" atl="sample"></td>
</tr>
<tr>
<td>detail</td>
<td><a href="http://xxx/">more..</a></td>
</tr>
</table>

<div class="btn">aaa</div>
<div class="btn">bbb</div>

</body>
</html>
例えば、タイトルを取得するのは、 下記のように取れるようである。
<?php

//require
require_once('phpQuery-onefile.php');

//ページ取得
$html = file_get_contents('./test.html');

//DOM取得
$doc = phpQuery::newDocument($html);

//要素取得
echo $doc["title"]->text();
なお、要素は、pq()メソッドを使って、
echo pq("title")->text();
と書くこともできるようです。
pq()は、jQueryの$()と等価であると共に、DOMをphpQueryで処理できるよう構造化する機能もあるようです。
基本的な使い方
$doc[""](もしくはpq(""))の""の中身はjQueryセレクタが使えるので、基本、jQueryと同じように利用すればいいのですが、ちょっとしたクセもあるので、基本的な使い方のメモ。
必要な要素にidが振られていれば、操作に苦労はあまりないのですが、一意なidが振られていない場合は、要素の指定に少々工夫が必要です。
idで指定する

echo $doc["#greeting"]->text();
classで指定する

クラスの場合は複数の要素が存在するので、必要に応じて取得する要素を限定するようにします。
echo $doc[".btn:eq(1)"]->text();
静的に指定する

先述のHTMLには2行2列の2つのテーブルがあります。その2つ目のテーブルの1行目の2列目の要素(tokyo)を取得する場合は、
echo $doc["table:eq(1) tr:eq(0) td:eq(1)"]->text();
のように指定する。find()を利用して、
echo $doc["table:eq(1)"]->find("td:eq(1)")->text();

とも書けるようす。
foreachで回す

複数の値が返ってくる要素は、原則としてforeach()が利用できるようです。
ただ、tableの場合は、各td要素の値を個別に取得することができないようで、あまり使い勝手は良くありません。
foreach($doc["table:eq(0) tr"] as $row)
{
//各要素取得
$key = pq($row)->find("td:eq(0)")->text();
$value = pq($row)->find("td:eq(1)")->text();

//表示
echo $key . " " . $value . "<br>";
}
実務では、後述のcontains等を利用するこの方が多くなります。
利用サンプル
ここでは実開発で利用するサンプルを随時の記述していきます。
特定の行の値を抽出

実運用では、テーブルの場合、特定の要素を抜く需要が多いかとおもいます。
例えばemailという項目を値を取得する場合は、containsと+を利用して、となりの要素を指定したりします。
echo $doc["table"]->find("td:contains('email') + td")->text();
HTMLをそのまま取得

->text()ではなく、->html()を利用すれば、そのままのHTMLが取得できる。
$html = $doc["table:eq(1)"]->find("td:contains('image') + td")->html();
アトリビュートを取得

$src = $doc["table:eq(1)"]->find("td:contains('image') + td img")->attr("src");
$href = $doc["table:eq(1)"]->find("td:contains('detail') + td a")->attr("href");