Asial Blog

Recruit! Asialで一緒に働きませんか?

Webスクレイピングが捗るGoutteを使ってみる

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
PHP
Goutte
Webスクレイピング


シャワー後の水切りでヘドバンしてたら頸椎を痛めてしまいました。あれは絶対やめた方がいいです。と周囲に広めているたきゃはしです。急に本題ですが今回はPHPで簡単にできるWebスクレイピングをご紹介します。

◯ Webスクレイピングとは



Webサイトからデータを抽出するソフトウェア技術のことです。
RSSやWebAPIが公開されていないサイトからでもデータ抽出が出来るようなイメージです。

早速クローラーの Goutte(グットゥ) を使って紹介していきたいと思います。
Goutte は Symfony や Twig、Pimple等の開発者として知られるFabienが手がける人気ライブラリです。

◯ インストール


  1. php composer.phar require fabpot/goutte:~2.0
インストールが完了したら vendor/fabpot/goutte/composer.json を覗いてみます...
  1. ...
  2.   "require": {
  3.     "php": ">=5.4.0",
  4.     "symfony/browser-kit": "~2.1",
  5.     "symfony/css-selector": "~2.1",
  6.     "symfony/dom-crawler": "~2.1",
  7.     "guzzlehttp/guzzle": "4.*"
  8.   },
  9.   ...
BrowserKit に Guzzle とくればどんな機能があるかなんとなく想像がつきますね。
話が逸れますが、使用するライブラリの依存コンポーネントを確認しておくことは大切かなと思います。

◯ 使ってみる



  1. <?php
  2. // first.php
  3. require_once './vendor/autoload.php';
  4.  
  5. $client = new Goutte\Client();
  6. $crawler = $client->request('GET', 'http://blog.asial.co.jp/');
  7.  
  8. // 抽出
  9. $targetSelector = 'h2.lh1_2em'; // アシアルブログの見出しのセレクター
  10. $crawler->filter($target)->each(function ($node) {
  11.     echo $node->text() . "\n";
  12. });
  1. ~/Sites/prac/goutte  php first.php
  2.   外部コンテンツをiframeサイズで拡大縮小させたり、固定幅コンテンツをウィンドウサイズでピッタリ表示させる方法
  3.   「Monaca for Hybridcast」CEATEC JAPAN 2014(2014/10/7-11開催)にて展示
  4.   お絵描きアプリと画像の保存処理の実装
  5.   PC/スマホでは無いWEBアプリ開発の話 -ハイブリッドキャスト編-
  6.   IllustratorでSVGファイルを保存してみました
  7.   iPhone6 PlusのPSDモックアップとAppleのフォント
  8.   Canvas Fingerprintingというトラッキング技術
  9.   HTML5プロフェッショナル認定試験のセミナー資料を公開します
  10.   弊社田中のコラム「エンタープライズHTML5とバックエンド─エンタープライズ×モバイルアプリ開発の最新動向」が公開されました
  11.   HTML5+CSS3+JSでネイティブGUIアプリが作れる、node-webkitを触ってみる
試しにアシアルブログの記事タイトルを抽出してみました。実質10行も書いてないです。
コードにしれっと出てきた $client と $crawler について確認してみたいと思います。

・$client Goutte\Client

  1. Array
  2. (
  3.     [0] => setClient
  4.     [1] => getClient
  5.     [2] => setHeader
  6.     [3] => removeHeader
  7.     [4] => setAuth
  8.     [5] => resetAuth
  9.     [6] => __construct
  10.     [7] => followRedirects
  11.     [8] => setMaxRedirects
  12.     [9] => insulate
  13.     [10] => setServerParameters
  14.     [11] => setServerParameter
  15.     [12] => getServerParameter
  16.     [13] => getHistory
  17.     [14] => getCookieJar
  18.     [15] => getCrawler
  19.     [16] => getInternalResponse
  20.     [17] => getResponse
  21.     [18] => getInternalRequest
  22.     [19] => getRequest
  23.     [20] => click
  24.     [21] => submit
  25.     [22] => request
  26.     [23] => back
  27.     [24] => forward
  28.     [25] => reload
  29.     [26] => followRedirect
  30.     [27] => restart
  31. )
定義されているメソッドのほとんどがsetter&getterですね。
このオブジェクトの役割はブラウザもしくはページそのものみたいな感覚でいいかなと思います。
click, submit, request, back, forward, reload, restart あたり覚えておけば良さそうです。

・$crawler Symfony\Component\DomCrawler\Crawler

  1. Array
  2. (
  3.     [0] => __construct
  4.     [1] => clear
  5.     [2] => add
  6.     [3] => addContent
  7.     [4] => addHtmlContent
  8.     [5] => addXmlContent
  9.     [6] => addDocument
  10.     [7] => addNodeList
  11.     [8] => addNodes
  12.     [9] => addNode
  13.     [10] => eq
  14.     [11] => each
  15.     [12] => reduce
  16.     [13] => first
  17.     [14] => last
  18.     [15] => siblings
  19.     [16] => nextAll
  20.     [17] => previousAll
  21.     [18] => parents
  22.     [19] => children
  23.     [20] => attr
  24.     [21] => text
  25.     [22] => html
  26.     [23] => extract
  27.     [24] => filterXPath
  28.     [25] => filter
  29.     [26] => selectLink
  30.     [27] => selectButton
  31.     [28] => link
  32.     [29] => links
  33.     [30] => form
  34.     [31] => setDefaultNamespacePrefix
  35.     [32] => registerNamespace
  36.     [33] => xpathLiteral
  37.     [34] => getNode
  38.     [35] => attach
  39.     [36] => detach
  40.     [37] => contains
  41.     [38] => addAll
  42.     [39] => removeAll
  43.     [40] => removeAllExcept
  44.     [41] => getInfo
  45.     [42] => setInfo
  46.     [43] => getHash
  47.     [44] => count
  48.     [45] => rewind
  49.     [46] => valid
  50.     [47] => key
  51.     [48] => current
  52.     [49] => next
  53.     [50] => unserialize
  54.     [51] => serialize
  55.     [52] => offsetExists
  56.     [53] => offsetSet
  57.     [54] => offsetUnset
  58.     [55] => offsetGet
  59. )
見た感じ配列のユーティリティクラスを継承してるようですね。
クローラーオブジェクトの役割はデータ抽出の作業者でよさそうです。
eq, attr, text, html, extract, selectLink, selectButton, link, links, form あたり覚えておけば良さそうです。
配列操作だと each, first, last, filter, contains, count あたりはよく使いそうですね。

さて、クライアントやクローラの内容をちょこっと予習できたところで、他の操作もしてみたいと思います。

・テキストリンクをクリックする


  1. <?php
  2. ...
  3. // リンクのテキストをクリックする
  4. $targetLinkText = 'バックエンド(プログラミング)';
  5. $link = $crawler->selectLink($targetLinkText)->link();
  6. $crawler = $client->click($link);
ページの遷移に関しては同期処理となっていてWaitを実装する必要はなさそうです。

・ボタンをクリックする


  1. <?php
  2. ...
  3. // ボタンのテキストをクリックする
  4. $targetButtonText = '検索';
  5. $button = $crawler->selectButton($targetButtonText)->form();
  6. $crawler = $client->click($button);
なんとブログにbuttonタグがありませんでした。。。なかったのですが
inputタグでも出来ることが分かって良かったです。(type=imageのaltテキストを対象にしました。)

・フォームを送信する


  1. <?php
  2. ...
  3. // 指定テキストで検索する
  4. $targetButtonText = '検索';
  5. $form = $crawler->selectButton($targetButtonText)->form();
  6. $searchParameters = ['words' => 'Monaca'];
  7. $crawler = $client->submit($form, $searchParameters);
formタグのactionやmethodを使用して処理してくれます。

また$clientはリクエスト&レスポンスを持っていました。以下のメソッドが定義されています。

・Symfony\Component\BrowserKit\Request

  1. Array
  2. (
  3.     [0] => __construct
  4.     [1] => getUri
  5.     [2] => getMethod
  6.     [3] => getParameters
  7.     [4] => getFiles
  8.     [5] => getCookies
  9.     [6] => getServer
  10.     [7] => getContent
  11. )
・Symfony\Component\BrowserKit\Response

  1. Array
  2. (
  3.     [0] => __construct
  4.     [1] => __toString
  5.     [2] => getContent
  6.     [3] => getStatus
  7.     [4] => getHeaders
  8.     [5] => getHeader
  9. )
「なるほどぉ〜」くらいに思ってもらえればと思います。

また記事作成中に知った機能ですが、Chrome Developer Tools > Copy CSS Path(画像参照) がセレクター取得に便利でした。


◯ まとめ



Goutteでスクレイピングしてみて良かったなぁという点は...



  • めちゃくちゃ簡単にスクレイピングできること。

  • 手作業に比べて圧倒的に高速かつ正確なこと。

  • 人間が詳細な作業内容を覚える必要がないこと。

  • 作業という単位でコンポーネント化しやすいので開発がどんどん楽になりそうなこと。

  • ちょっとずれるけど、PHPUnitと連携すれば画面テストにも利用できること。


逆に課題かなぁと感じた点は...



  • プログラムがWebサイトの稼働状況やDOM構造に依存しているため安定稼働が難しいこと。

  • 自動化する場合は対象のDOM構造が変更されていないか監視する機能が必要なこと。

  • 変更があれば効率よく開発者へ通知するためのロギング機能や通知処理が必要なこと。

  • 変更があれば抽出ミスが発生するため、エラー処理やリカバリが必要なこと。


要約すると、すごい便利なんだけど作りこまないと運用大変そうだなぁという感じでした。

今更言うことではないですが、規約に反する行為および抽出データの取り扱いにはくれぐれもご注意を。
それでは皆様、健全で生産的なスクレイピングを楽しみましょう!