Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
106
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@zaburo

今更ながら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");
106
Help us understand the problem. What is going on with this article?
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
106
Help us understand the problem. What is going on with this article?