今更ながらPHPでスクレイピングをしてみる

More than 3 years have passed since last update.

以前は、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");