【phpQuery】で簡単WEBスクレイピング!をしてみた。
GWも終わってしまい、なんと6月は祝日がありません!7月の海の日まで祝日が無い絶望感に包まれた時にエンジニアがやりたくなる事と言えば・・・
そう!WEBスクレイピングですね!
今回はPHPで手軽にスクレイピングする方法を書いていきます。
仕事の8割は検索と徘徊に費やす系エンジニア田中です。
「スクレイピング」とか「クローリング」って何?
そもそもスクレイピングとは何なのか?Google翻訳さんによると「こすること」と出ます。
ちなみに「クローリング」は「巡回すること」です。
イメージとしては、WEB上にあるHTMLコンテンツを「クローリング(巡回)」して、欲しい情報を「スクレイピング(擦り取る)」という感じです。
「抽出」にあたる「Extract」とかを使わない辺りが、エンジニアの執念の垣間見える作業だということを物語っていますねw
用意するもの
- PHPが使える
- ネットに繋がる
- 優秀な頭脳
これだけあればスクレイピングの準備はばっちりです。
参考にするサイト
今回はPHPでjQueryっぽくHTMLを操作出来る「phpQuery」を使います。
https://code.google.com/p/phpquery/
ソースを取ってくる
phpQueryを使うのはとっても簡単です。
ソースを取ってきてrequire_once("phpQuery-onefile.php");
で完了です。
https://code.google.com/p/phpquery/downloads/list
ここの中の最新の「phpQuery-*.*.*.*-onefile.zip」をクリックします。
ダウンロードのリンクが表示されるので右クリックでリンク先をコピーして
1 |
wget https://phpquery.googlecode.com/files/phpQuery-*.*.*.*-onefile.zip |
でzipを取ってきます。
取ってきたzipはunzipでサクッと解凍!
1 |
unzip phpQuery-*.*.*.*-onefile.zip |
そうすると「phpQuery-onefile.php」というファイルが出来ているのでこれでOKです。
さっそく使ってみる
今回は弊社ブログ記事のタイトルと中の小見出しを抽出してみます。
まずは各記事のリンク先を抽出します。
これを取ってくるのは便利な「file_get_contents」という関数です。
ローカルにあるファイルはもちろん、URLを引数に渡してあげるとHTMLを取得する事が出来ちゃういます。
第二引数にプロキシを設定する事も出来ますが今回は割愛です。詳しくは公式リファレンスにてご確認下さい。
http://php.net/manual/ja/function.file-get-contents.php
1 2 3 4 5 6 7 8 9 10 |
<?php // phpQueryの読み込み require_once("phpQuery-onefile.php"); // HTMLデータを取得する $HTMLData = file_get_contents('https://person-link.co.jp/'); // HTMLをオブジェクトとして扱う $phpQueryObj = phpQuery::newDocument($HTMLData); ?> |
とりあえずこんな感じです。
あとは$phpQueryObj
からjQueryっぽくDOMを取得してくるだけです。
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php // 続きから // h1タグを片っ端から取得 $titleArr = $phpQueryObj['h1']; // ただの配列なのでぶん回す foreach($titleArr as $val) { // pq()メソッドでオブジェクトとして再設定しつつさらに下ってhrefを取得 echo pq($val)->find('a')->attr('href').PHP_EOL; } ?> |
今回地味にはまったのがここです。
取得してきた$titleArrの要素はDOMElementとして認識されていて、phpQueryの支配下から外れてしまうんですね。
http://php.net/manual/ja/class.domelement.php
なのでphpQueryのメソッドを使える様に「pq()」メソッドで再定義してあげた上で中の要素を取得する、という小技が必要でした。(ハマり目安:時間5分)
子ページを取得する
ここまで理解が出来ればあとは簡単!
同じ要領で子ページのタイトルを取得してきましょう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php /** * もろもろ競合しちゃうと嫌なので関数化 * 小見出しを取得して出力 * @param string $url 子ページのURL */ function getChiledPage($url) { // ページを取得してオブジェクト化! $phpQueryObj = phpQuery::newDocument(file_get_contents($url)); // ループでぶん回す foreach($phpQueryObj['h2'] as $i => $val) { $komidashi = pq($val)->text(); echo '小見出し'. $i .':' . $komidashi . PHP_EOL; } } ?> |
整える
あとは作った関数の呼び出し元をちゃんと整理して、コンソールにぼこぼこ出力されるようにしましょう!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php // phpQueryの読み込み require_once("phpQuery-onefile.php"); // HTMLデータを取得する $HTMLData = file_get_contents('https://person-link.co.jp/'); // HTMLをオブジェクトとして扱う $phpQueryObj = phpQuery::newDocument($HTMLData); // h1タグを片っ端からぶん回す foreach($phpQueryObj['h1'] as $val) { // 連続実行すると怒られちゃうのでとりあえず5秒待機 sleep(5); // pq()メソッドでオブジェクトとして再設定しつつさらに下ってhrefを取得 $title = pq($val)->find('a')->text(); $url = pq($val)->find('a')->attr('href'); echo 'タイトル:' . $title . PHP_EOL; echo 'ページURL:'. $url . PHP_EOL; getChiledPage($url); echo PHP_EOL.PHP_EOL; } /** * もろもろ競合しちゃうと嫌なので関数化 * 小見出しを取得して出力 * @param string $url 子ページのURL */ function getChiledPage($url) { // ページを取得してオブジェクト化! $phpQueryObj = phpQuery::newDocument(file_get_contents($url)); // ループでぶん回す foreach($phpQueryObj['h2'] as $i => $val) { $komidashi = pq($val)->text(); echo '小見出し['. $i .']:' . $komidashi . PHP_EOL; } } ?> |
関数の呼び出し元のforeachの中に書いてある「sleep(5)」はとても大切で、弊社のHPのように弱小インフラで運用しているサービスは、連続で多数のリクエストを要求されると簡単にぶっ飛びますw
本当は外に出した関数の中でechoするのは好ましく無いとか変数名が雑とか色々ありますが、個人の趣味程度であればこれで勘弁して欲しいところですw
悪用は厳禁なんだよ!
情報を素早く集めるために、WEBを使いプログラムを使い、とても便利な世の中になりましたが、もちろんこれは人類の文化的な発展のために作られたものですので、XXXがXXXをXXXするために使ったりしたらいけないんです!
連続リクエストによって偽計業務妨害容疑で逮捕されたという事件がありますので、本当にリクエストの回数と間隔にはお気を付け下さいませ。
秒間1リクエストまでになるように考慮してもサーバーがぶっ飛べば逮捕される恐れがあるということです。
くれぐれも迷惑をかけないように気を付けましょう!
おわり
ルールとモラルを守って次の祝日まで楽しいスクレイピングライフをお送りくださいませ!