
はじめに
スクレイピングをする際、以下のようにセレクタで要素を選び、取得する内容や属性を指定するやり方が最も基本的な方法です。
1 |
$entry_title = $crawler->filter('h1.entry-title')->text(); |
※phpのライブラリ「Goutte」を使って説明します。
オブジェクト内に要素自体が存在しない場合等のエラーは回避策があるので問題を解消できますが、コーディングでは回避できない問題点があります。
問題点:テーブルの行数の変化に対応できない
例えば複数ページに渡って以下のようなテーブル要素があったとします。このテーブル内の「出身」の情報のみを抜き出したい場合に
性別 | 男性 |
---|---|
年齢 | 30歳 |
出身 | 東京都 |
趣味 | サッカー |
以下のような記述になると思います。(id、classがtable内に存在しないケースを想定)
1 |
$entry_title = $crawler->filter('table td')->eq(3)->text(); |
ただし、このテーブル内の行数が必ずしも全ページで同じとは限りません。よくあるケースですが空の値の項目は可読性を高めるために非表示されていたりします。
性別 | 男性 |
---|---|
年齢 | 30歳 |
趣味 | サッカー |
この場合、上記のコードでは趣味の情報を取得してしまうことになります。行数のチェックをif分岐する方法もありますが複雑なパターンになればなるほど一苦労です。取得ページ数が大量にある場合、パターンの確認を行うのも骨の折れる作業です。
解決策
phpのgoutteの場合、extractという非常に便利な関数が用意されています。指定した要素のテキストや属性の値を一度にすべて取得して配列に格納してくれる優れものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//table内のth列とtd列に対し、それぞれでextractし、項目名用の配列と値用の配列を作ります。 $th_list = $crawler->filter('table')->filter('th')->count() != 0 ? $crawler->filter('table')->filter('th')->extract("_text") : null; $td_list = $crawler->filter('table')->filter('td')->count() != 0 ? $crawler->filter('table')->filter('td')->extract("_text") : null; //取得したい情報名を設定します。 $target = array('職種', '資格', '勤務地'); //項目名用の配列と取得したい情報名の配列を入れ子にし、総当たりのチェックします。 if (!empty($th_list) { foreach ($th_list as $key => $value) { foreach ($target as $key2 => $value2) { if($value === $value2){ $result[$value2] = trim($td_list[$key]); break; } } } } |
キー名に日本語を使用していますが違和感のある方は以下のようにすれば問題ありません(ループの中で必要な値を取り出してください)
1 2 3 4 5 |
$target = array( array('th' => '職種','key' => 'job'), array('th' => '資格','key' => 'sikakau'), array('th' => '勤務地','key' => 'kinmuchi') ); |
※dl要素に対しても同じように使えます。
まとめ
いかがでしょうか。コード量もだいぶすっきりします。予期せぬエラーを引き起こさないためにもよほどの理由がない限り、この方法で取得するのがベターです。