PHP "Simple HTML DOM Parser"の使い方メモ

概要


卒研のためにWEBサイトから必要な情報だけ抽出したデータセットを作成したときに用いた、PHPでHTMLを簡単にパースできるというPHP Simple HTML DOM Parserの使い方の備忘記録。




参考ページ


PHPでHTMLをパースする


抽出対称


amazonのベストセラーのランキングページ2009年の本のベストセラーから

  • ISBN-10
  • 本のタイトル
  • 著者

を取得する。
※ 2010/12/29 時点でのhtmlには対応している。


本のタイトル・著者 抽出スクリプト

<?php
// ランキングページの1ページ目のURL(1位~20位)
$page_url = "http://www.amazon.co.jp/gp/bestsellers/2009/books/ref=pd_ts_pg_1?ie=UTF8&pg=1";
// ランキングページのHTMLを取得
$page_data = mb_convert_encoding(file_get_contents($page_url),'UTF-8','auto');

// simple_html_dom.phpを読み込む
require_once 'simplehtmldom/simple_html_dom.php';
// 取得したhtmlのsimple_html_domオブジェクトを作成
$shd_obj = str_get_html($page_data);

var_dump($shd_obj)
ちなみに $shd_obj を var_dump() してみるとこんな感じ

object(simple_html_dom)#1 (18) {
  ["root"]=>
  object(simple_html_dom_node)#2 (8) {
    ["nodetype"]=>
    int(5)
    ["tag"]=>
    string(4) "root"
    ["attr"]=>
    array(0) {
    }
    ["children"]=>
    array(2) {
      [0]=>
      object(simple_html_dom_node)#4 (8) {
        ["nodetype"]=>
        int(6)
        ["tag"]=>
        string(7) "unknown"
        ["attr"]=>
        array(0) {
        }
        ["children"]=>
        array(0) {
        }
        ["nodes"]=>
        array(0) {
        }
        ["parent"]=>
        object(simple_html_dom_node)#2 (8) {
          ["nodetype"]=>
          int(5)
          ["tag"]=>
          string(4) "root"
          ["attr"]=>
          array(0) {
          }
          ["children"]=>
          array(2) {
            [0]=>
            object(simple_html_dom_node)#4 (8) {
              ["nodetype"]=>
              int(6)
              ["tag"]=>
              string(7) "unknown"
              ["attr"]=>
              array(0) {
              }
              ["children"]=>
              array(0) {
              }
              ["nodes"]=>
              array(0) {
              }
              ["parent"]=>
              *RECURSION*
              ["_"]=>
              array(2) {
                [0]=>
                int(2)
                [4]=>
                string(15) ""
              }
              ["dom":"simple_html_dom_node":private]=>
              object(simple_html_dom)#1 (18) {
                ["root"]=>
                *RECURSION*
                ["nodes"]=>
                array(2922) {
                  [0]=>
                  *RECURSION*
                  [1]=>
                  object(simple_html_dom_node)#3 (8) {
                    ["nodetype"]=>
                    int(3)
                    ["tag"]=>
                    string(4) "text"
                    ["attr"]=>
                    array(0) {
                    }
                    ["children"]=>
                    array(0) {
                    }
                    ["nodes"]=>
                    array(0) {
                    }
                    ["parent"]=>
                    *RECURSION*
                    ["_"]=>
                    array(1) {
                      [4]=>
.
.
.

simple_html_dom_node というオブジェクトがhtmlタグごとに作成され,そのタグがchildren-子ノード(持っているhtmlタグ)の情報、parent-親ノード(所属している一つ上の階層のhtmlタグ)の情報を所持している。
のですべてを var_dump() するととてつもない量になるので注意が必要。


simple_html_dom オブジェクトを作成した後の続きのコード

<?php
/*
 * さっきコードの続き
 */
// 本情報を格納するための配列を用意
$book_info = array();

foreach ($shd_obj->find("td.zg_productInfo ") as $element) {
        // <td class="zg_productInfo">要素内のhtmlを取得
        $inner_data = $element->innertext;

        // 本の情報を抽出する
        $book_detail_obj = $element->find("div.productTitle a");
        $book_info['title'][] = $book_detail_obj[0]->innertext;
        if (isset($book_detail_obj[1]->innertext)) {
                $book_info['auther'][] = $book_detail_obj[1]->innertext;
        } else {
                $book_auther_obj = $element->find("div.productTitle div.byLine");
                $book_info['auther'][] = trim($book_auther_obj[0]->innertext);
        }
}

simple_html_dom_node->find() メソッド
simple_html_dom_node->find() メソッドの引数にhtmlタグを指定すると、返り値が配列になり、その指定したhtmlタグが存在するだけ、配列の要素(simple_html_dom_node オブジェクト)を作成する。
また、class,id属性や入れ子のhtmlタグを指定すれば絞り込むことができる。上の例のforeach中のfind()メソッドの例では

$element->find("div.productTitle a")

と引数を指定しているので、$element要素がもつ<div>タグのclass属性がproductTitleであり、かつその<div>タグの子要素<a>の情報に絞り込んでいる。


simple_html_dom_node->innertext 属性
simpole_html_dom_node->innertext属性はそのhtmlタグが囲んでいる中身のhtmlを保持している。


var_dump($element)
ちなみに$elementを var_dump() するとこんな感じ

object(simple_html_dom_node)#766 (8) {
  ["nodetype"]=>
  int(1)
  ["tag"]=>
  string(2) "td"
  ["attr"]=>
  array(1) {
    ["class"]=>
    string(14) "zg_productInfo"
  }
  ["children"]=>
  array(8) {
    [0]=>
    object(simple_html_dom_node)#768 (8) {
      ["nodetype"]=>
      int(1)
      ["tag"]=>
      string(3) "div"
      ["attr"]=>
      array(1) {
        ["class"]=>
        string(12) "productTitle"
      }
      ["children"]=>
      array(2) {
        [0]=>
        object(simple_html_dom_node)#769 (8) {
          ["nodetype"]=>
          int(1)
          ["tag"]=>
          string(1) "a"
          ["attr"]=>
          array(1) {
            ["href"]=>
            string(117) "http://www.amazon.co.jp/1Q84-BOOK-1-%E6%9D%91%E4%B8%8A-%E6%98%A5%E6%A8%B9/dp/4103534222/ref=pd_ts_b_1?ie=UTF8&s=books"
          }
          ["children"]=>
          array(0) {
          }
          ["nodes"]=>
          array(1) {
            [0]=>
            object(simple_html_dom_node)#770 (8) {
              ["nodetype"]=>
              int(3)
              ["tag"]=>
              string(4) "text"
              ["attr"]=>
              array(0) {
              }
              ["children"]=>
              array(0) {
              }
              ["nodes"]=>
              array(0) {
              }
              ["parent"]=>
              object(simple_html_dom_node)#769 (8) {
                ["nodetype"]=>
                int(1)
                ["tag"]=>
                string(1) "a"
                ["attr"]=>
                array(1) {
                  ["href"]=>
                  string(117) "http://www.amazon.co.jp/1Q84-BOOK-1-%E6%9D%91%E4%B8%8A-%E6%98%A5%E6%A8%B9/dp/4103534222/ref=pd_ts_b_1?ie=UTF8&s=books"
                }
                ["children"]=>
                array(0) {
                }
                ["nodes"]=>
                array(1) {
                  [0]=>
                  object(simple_html_dom_node)#770 (8) {
                    ["nodetype"]=>
                    int(3)
                    ["tag"]=>
                    string(4) "text"
                    ["attr"]=>
                    array(0) {
                    }
                    ["children"]=>
                    array(0) {
                    }
                    ["nodes"]=>
                    array(0) {
.
.
.

<div class="productTitle">
ちなみにdiv.productTitleの一つ目の要素を示すとこんな感じのhtml。

<div class="productTitle"><a href="http://www.amazon.co.jp/1Q84-BOOK-1-%E6%9D%91%E4%B8%8A-%E6%98%A5%E6%A8%B9/dp/4103534222/ref=pd_ts_b_1?ie=UTF8&s=books">1Q84 BOOK 1</a> <div class="byLine"> 
 
 
 
 
 <a href="/村上-春樹/e/B000AP7AFI/ref=pd_ts_b_1_1">村上 春樹</a> (著)</div></div>

このようなhtmlになっているので、

  • 一つ目の<a>要素内には本のタイトル
$book_info['title'][] = $book_detail_obj[0]->innertext;
  • 二つ目の<a>要素内には本の著者
$book_info['auther'][] = $book_detail_obj[1]->innertext;

のようにして取得している。
ちなみに、2つ目の<a>要素がない場合があるため、スクリプトのような処理をしている。

ISBN-10 の取得は正規表現を使った

$page_dataは一番最初スクリプトのfile_get_contentsで取得したものを利用している。

<?php
$isbn10_get_pattern = '/<span class="crAvgStars" style="white-space:no-wrap;"><span class="asinReviewsSummary" name="(.+)" ref="pd_ts_b_/';

preg_match_all($isbn10_get_pattern, $page_data, $book_ids, PREG_SET_ORDER);
$isbn10s = array();
foreach($book_ids as $book_id){
        $isbn10s[] = $book_id[1];
}      

まとめ

simple_html_domと正規表現を組み合わせて用いることで、より簡単に情報を抽出できる!
simple_html_domって簡単だし素晴らしい。