Help us understand the problem. What is going on with this article?

今更ながら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");
zaburo
こんにちは。自分用のメモをだらだら公開しています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした