あ、どうも僕です。
今日はPHPでWebサイトをクローリングしてjQueryチックにDOMを操作して、データを取得したり、画像などをダウンロードする方法について書こうと思うよ!
これができると何がうれしいかって、いつも手動で巡回しているサイトなんかにアップされてる画像データを自動でダウンロードできるようになるんだよね。
人がやっていた作業をコマンド一発で自動化できるから、捗ること間違いなしだよ!
あれ、この人、この前node.js使って同じことやってなかったっけか?
node.jsを使ってjQueryチックにWebサイトをクローリングする方法
なんで同じことをPHPでやってんだ?とか思っていやしないだろうか。ああそうさ!ついこの前、node.jsで全く同じことをやって記事を書いたさ!なぜかって!?それは、node.jsの非同期地獄にやられたからさ!
やつめ、データのダウンロードからデータベースの更新まで全部非同期でやりやがんの!そんでもって、全部フリーダムなタイミングで実行されるから、もうしっちゃかめっちゃか!おまけにリソース不足に陥ってプログラムごと落ちるという残念な有様!あえてガンダムに例えるなら、node.jsはニューガンダムだね!勝手にファンネルがビュンビュン飛んでって敵を撃ち落とすわけだが、それを制御できなければ、自ら放ったファンネルに打たれて自滅するのさ!そうだ!僕は、ニュータイプではなかったのだ!単純に同期処理を書けばすむ話なのだが、こちとら仕事でやってんだ!そう、時間は有限なのだ!ニュータイプの種が覚醒するまで待ってられないんだよ!そこまで深入りするより、別の言語で書き直した方が早いと判断したわけさ!そんでもって、性懲りもなくそれをまた記事にしてるってわけさ!
ここまで書くと、node.jsをディスってるように聞こえるが、やつはちょっとした処理をさくっと書く分にはとっても高いパフォーマンスを発揮するのだ。ただ、ちょっと入り組んだ処理を書こうもんなら、非同期時刻に陥って大変なことになる。同じ苦しみを味わった同志は少なくあるまい。いつかの再戦を胸に誓いつつ、使い慣れた言語で同じことができないかと調べてみた結果、あっさりできちゃったわけさ。
べつにjQueryチックにDOMを扱いたいからって、JavaScriptにこだわる必要はないんだよね。どっちを使うかはお好み次第だけど、今日はPHPでそれを実現する方法を紹介しようではないか。
ライブラリを導入する
HTMLは完全なXMLじゃないから、構文的にDOMを扱うのは大変だろうと思っていたのだが、調べたら意外とあった。HTMLをjQueryチックにDOMを操れるライブラリが。
今回は「simple_html_dom」を使った方法を紹介するよ!
その名の通り、とってもシンプルにHTMLをDOMで扱えてしまう優れものさ!
まずは公式サイトからコード一式をダウンロードしてきます。
そして、中身を展開して、あなたのコードにRequire!
require_once dirname(__FILE__) . '/simplehtmldom/simple_html_dom.php';
たったこれだけ。ああ簡単!
ちなみに、同じようなライブラリは他にもあって、「phpQuery」という、その名もズバリ、phpでjQueryな記述ができてしまうライブラリ。でもこれ試したんだけど、正しい結果返してこないんだよね。ページにもよるのかもしれないけど、正しく動かないのでは話にならない。今回は華麗にスルーさ!
HTMLを解析してみよう!
では、「simple_html_dom」を使ってWeb上のHTMLをパースしてみようではないか!
Web上のHTMLを取得して、DOMにする方法は以下の通り。
$doc = file_get_html($url);
ああ簡単!1行だよ!中ではきっと、Web上のドキュメントを取得するためにソケット通信したり、不完全なXMLであるHTMLを補完してうまい具合に解析したり、それをインスタンス化してDOMツリーにしたりいろいろ大変なことをしているわけではあるが、それがこの1行ですよ!名前に違わずシンプルかつスマートなやつだぜ!
そして、そのDOMツリーから目的のノードを見つけてくるのも1行でかけちゃう!
$objects = $doc->find('div.shoplist ul');
これで、対象のノードが変数に格納されたわけだ。
そこから、必要な情報を取り出すためには、こんな感じで子ノードに潜り込んだり、
$objects[0]->children[1]->children[0]->children[1]->innertext();
こんな感じで属性からリンクを取得したり、
$objects[0]->attr['href'];
こんな感じで画像のパスを取得したりできるわけさ。
$objects[0]->attr['src'];
目的の値がどこに格納されているかは、findで取得したインスタンスを「var_dump()」で中身のぞけば全部書いてあるから、あとは連想配列を扱う感覚で探っていけばOK。とっても簡単かつシンプルだ!
ファイルを自動ダウンロード
そんでもって、画像パスさえ取得できれば、以下のコードでさくっとダウンロードして、あなたが日課としていたエ○画像収集作業も自動化できちゃうわけさ!
$destDir = './' . $imageId . '/';
if (!file_exists($destDir)) {
mkdir($destDir, 0755, true); // Make directory recorsivery
}
$destPath = $destDir . $fileName;
FileDownloader::saveTargetFile($imageUrl, $destPath);
/**
* File download class.
*/
class FileDownloader {
/**
* Save target file.
* @param string $srcPath source path
* @param string $destPath dest path
*/
public static function saveTargetFile($srcPath, $destPath) {
$contents = self::getFileContents($srcPath);
self::putFileContents($destPath, $contents);
}
/**
* Get file contents.
* @param string $srcPath source file path
* @return contents instance
*/
private static function getFileContents($srcPath) {
if (!function_exists('file_get_contents')) {
$fhandle = fopen($srcPath, "r");
$fcontents = fread($fhandle, filesize($srcPath));
fclose($fhandle);
} else {
$fcontents = file_get_contents($srcPath);
}
return $fcontents;
}
/**
* Put file contents.
* @param string $destPath destination file path
* @param string $contents save file contents
*/
private static function putFileContents($destPath, $contents) {
if (!function_exists('file_put_contents')) {
$fhandle = fopen($destPath, "w");
$fcontents = fwrite($fhandle, $contents);
fclose($fhandle);
} else {
file_put_contents($destPath, $contents);
}
}
}
右クリック、保存。右クリック、保存。右クリック、保存。右クリック、保存。。の無為な時間はもっと有意義に使おう!クローラー万歳!
まとめ
っというわけで、今回はPHPでjQueryチックにWebサイトをクローリングする方法を紹介してみたよ!ファイルダウンロードを自動化する際に参考にしてもらえればと思う。これでなたも、日々の単純作業から解放されるってもんさ!
ちなみに、不用意なクローラの使用は対象のサーバに負荷をかけるから、適度にSleep処理を挟むとか、Webサービスならクローリングが許可されているかとか、クローラ向けに設置されている「robot.txt」のお作法に従うとか、お行儀よく使おうね!
以上、よっしーでした!
関係ないけど、最後にガンダムカラーを貼付けてさようならする。丸とか四角で適当な形書いて、この色で塗りつぶせば、何となく連邦の白いやつっぽくなるから、色のイメージってすてきだ。
R:230, G:230, B:230
R:117, G:000, B:255
R:225, G:206, B:000
R:225, G:038, B:038
R:225, G:038, B:038
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。