node.jsを使ってjQueryチックにWebサイトをクローリングする方法

node.jsを使ってjQueryチックにWebサイトをクローリングする方法

よっしー

よっしー

どうも僕です。

今日は、サーバサイドJavaScript(node.js)でjQueryチックにDOMを操作して、Webサイトをクローリングする方法についてまとめようと思うよ!

要は、既存のWebサイトから必要な情報を収集してきて、データベースに突っ込もうということ。

これができると何がうれしいかって、簡単にまとめサイトが作れるんだよね。
クローリングする方法は数々あれど、なにゆえサーバサイドJavaScriptのnode.jsを使ってこれをするかというと、「クローリング対象の部分をjQueryのセレクタで指定できる」のが大きいわけです。

みんな、好きだよね!jQuery!とっても便利だよね!jQuery!

好みにもよるかと思うが、JavaScriptのようなふにゃふにゃした言語をあえて使う理由は、クライアントサイドプログラミングを組むか、JQueryの恩恵を受けるためといっても過言ではあるまい。

あと、node.jsはシングルスレッドで動作するため、ApacheやTomcatのように複数のスレッドやプロセスを使用するミドルウェアに比べて、リソースが軽量で高速に動作するという利点があろう。

それ以外では、サーバサイドとクライアントサイドのアプリケーションを同じ言語でかけるという点か。

とりあえず、今回あえてnode.jsを使う理由としては、上記のうち、jQueryチックに簡単にスクレイピングできる点と、実行速度に期待するところが大きい。

それでは、実際に初めてみようではないか。

node.jsをインストールする

まずはnode.jsをインストールしよう。
これをインストールしなければ始まるまい。

公式のこちらのページで、「INSTALL」というボタンを押すと、環境に応じたインストーラがダウンロードできます。

Windowsならexeをぽん。
Macならdmgを実行するだけでインストールできます。

そんでもってコマンドプロンプトもしくはターミナルから以下のコマンドを打ち込んでみましょう。

node --version

バージョンが表示されればインストール成功です。

エラーになる場合は、node.jsにパスが通っているか疑ってかかろう。
プロンプトを再起動するか、システムの環境変数の設定でパスを通しましょう。

これができれば、あなたも立派なnode.js使いだ。
ようこそ、こちら側の世界へ。

「Hello World」を表示する

はれてnode.js使いになったわけだが、早速、初めての「Hello World」で、お、動いた!やったよ僕!感を味わってみようではないか。以下のコードを記述して、「hello.js」という名前で保存してみてくれ。

console.log('Hello World!');

そんでもって、コンソールで以下のコマンドをたたいて実行する。

node hello.js

画面に「Hello World!」と表示されれば成功だ!

Welcome to node.js world!

ちなみに「console」というインスタンスはnode.jsのデフォルトインスタンスで、コンソールにあれやこれやと表示するためのものだ。ほかの言語にもあるprint系のお約束と思ってもらっていい。

必要なモジュールをインストールする

node.js単体ではHello Worldと画面に表示する程度のことしかできないが(フルスクラッチでバリバリ書くなら別だが)、node.jsにはnpm(node.js package manager)という、PerlならCPAN、PHPならPEARに相当する、ライブラリのパッケージマネージャがあるのだ。npmはインストーラでインストールした場合はセットでインストールされていると思う。

インストールされていなかったら、泣こう。
今夜ぐらいは泣いても許されていいと思うよ。

インストールされてなかったらググってnpm単体でインストールする方法を調べてね。

さて、それでは、node.jsを拡張するパッケージをインストールしようではないか。

今回は以下のパッケージをインストールする。

  • request
  • mysql
  • sequelize
  • cheerio

「request」はHTMLでスクレイピング対象のURLのデータを取ってくるライブラリです。
「mysql」はデータをMySQLにぶち込むためのライブラリです。イッツスタンダード。
「sequelize」はO/Rマッパーライブラリです。どうせDBをさわるなら、O/Rマッパーを使おうではないか。
「cheerio」は取ってきたデータをjQueryチックにDOMのセレクタでいじいじするためのライブラリです。

DOMのセレクタをいじいじできるライブラリは「jsdom」や「node-jquery」などいくつか存在するのですが、今回なぜ「cheerio」を使うかというと、こちらのサイトで代表的なライブラリの速度比較がされていて、その中でも圧倒的な速度を示していたからだ。そして、俺がjQueryを再実装してやるという男前感にひかれたのだ。僕は男前が好きなのだ。

ただし、注意しないといけないのは、「cheerio」はjQueryの再実装であり、全ての機能をサポートしているわけではないということだ。単純に、DOMを操作する分には問題ないと思われるが、複雑な処理がしたいしたい場合は障害が出るかもしれないのでそこら編は覚悟しておいた方がよいかもしれない。

それではnpmをつかって上記のライブラリをインストールしよう。コンソールで以下のコマンドをたたく。

npm install request
npm install mysql
npm install sequelize
npm install cheerio

これで必要なライブラリはインストールされたはずです。ちゃんとインストールされているか確認してみましょう。

npm list

これで上記のパッケージがリストアップされていればインストール成功です。おめでとう。そして、ありがとう。

データベースにスキーマを定義しよう

実行環境はそろった。ライブラリもインストールした。それでは、早速コードを書いてみようではないか。

まずは、O/Rマッパーを利用してデータベースにスキーマを定義しよう。

データベースに接続するには、ORマッパーのSequelizeを使って、以下のように記述します。

// Initialize instance
var sequelize = require('sequelize');

// Connect to database
var connection = new sequelize('データベース名', 'ユーザ名', 'パスワード', {オプション: '値'});

第四引数にはオプションとしてJSON形式で以下の値が指定できます。

  • host(文字列):接続先ホスト(デフォルトはlocalhost)
  • port(数値):接続先ポート(デフォルトは3306)
  • logging(TRUE/false):SQLのログ出力を有効にするか
  • dialect(文字列):使用するSQL形式。mysqlまたはsqliteが指定可能(デフォルトはmysql)
  • storage(文字列):SQLiteの場合にストレージとして使うファイルのパス(デフォルトはインメモリデータベース[:memory:])
  • maxConcurrentQueries(数値):データベースへの最大同時発行クエリ数(デフォルトは50)

次に、モデルを作りましょう。モデルはデータベースのテーブルと1対1対応します。モデル定義の例は以下の通り。ここでは「sites」という名前でタイトルとURLのフィールドをもつモデルを定義してみました。

// Define models
var sites = connection.define('sites', {
	url: sequelize.TEXT,
	title: sequelize.STRING
});

第一引数にはテーブル名、第二引数にはJSON形式でフィールド名と型を定義します。定義できる型は以下の通り。

  • Sequelize.STRING:VARCHAR(255)
  • Sequelize.TEXT:TEXT
  • Sequelize.INTEGER:INTEGER
  • Sequelize.DATE:DATETIME
  • Sequelize.BOOLEAN:TINYINT(1)
  • Sequelize.FLOAT:FLOAT

このデータベースとの型の対応はPHPのフレームワークのCakePHPと一致してますね。ここで定義したデータベースの型をそのままCakePHP側で使えれば、バックエンドのスクリプトはnode.js。フロントエンドはCakePHPといった使い分けもできそうです。これ重要。

そして、データベースとシンクロすることで、自動でデータベースのテーブルが作成されます。まあ便利!全く、革命的と行っても過言ではない。いや、過言かもしれない。

// Synchronize to database
connection.sync({force: true});

それではこれらのコードを「database.js」とでも名前をつけて、実行してみましょう。そうすると、自動でテーブルが生成されます。生成されたテーブルには主キーとして「id」フィールドが自動で含まれています。主キーの名前もCakePHPと同じ。これ重要。

データベースのテーブル生成は初回の1回のみでOKです。モデルの定義が変更になった場合はこれを修正してもう一度実行しましょう。

ただし、登録されていたレコードは全て消去されるので気をつけましょう。
再構築の際にテーブルを消去したくない場合は、sync()の引数の「{force: true}」を取りましょう。

クローリングをやってみよう

データを格納すべきデータベースのテーブルはできた。では、本題のクローリングをやってみようではないか。

以下では米Googleサイトからタイトルを取ってきてデータベースに突っ込んでるサンプルプログラムだよ。

//Initialize instance
var sequelize = require("sequelize");

//Connect to database
var connection = new sequelize('データベース名', 'ユーザ名', 'パスワード', {オプション: '値'});

// Define models
var sites = connection.define("sites", {
	url: sequelize.TEXT,
	title: sequelize.STRING
});

// ----

//Initialize instance
var request = require("request");
var cheerio = require("cheerio");

// ----

// Define request url
var requestUrl = "http://www.google.com";

// Send http request
request({url: requestUrl}, function(error, response, body) {

	// If request succeed
	if (!error && response.statusCode == 200) {
		$ = cheerio.load(body); // Create cheerio instance

		// Get response data
		var url = response.request.href;
		var title = $("title").text();

		console.log(url);
		console.log(title);

		// Create new instance
		var site = sites.build();

		// Set fields
		site.url = url;
		site.title = title;

		// Save to database
		site.save()
			.success(function(anotherTask) {
				console.log('Succeed');
			})
			.error(function(error) {
				console.log(error);
			});
	}

	// If error occured
	else {
		console.log("--------------------------------------------------");
		if (error && "code" in error) {
			console.log("Error Code:" + error.code);
		}
		if (error && "errno" in error) {
			console.log("Error No:" + error.errno);
		}
		if (error && "syscall" in error) {
			console.log("Error Syscall:" + error.syscall);
		}
		if (response && "statusCode" in response) {
			console.log("Status Code:" +  response.statusCode);
		}
	}
});

29行目でcheerioのインスタンスを$という名前で作ります。JavaScriptは$も変数名として使えるのでこんなことができるのです。jQueryでも$(〜)とか書くけど、$ってJavaScript的に特殊文字じゃなくて、単なる変数名なんだよね。jQueryでも$にはjQueryの関数ポインタが入っています。それと同じことをこの行でやっているわけだね。これをやるときには「チェリオー!」って叫ぶんだよ!これ、絶対だからな!男と男の約束だぞ!

そんでもって33行目でjQueryチックにtitleタグの中身を取り出している。せっかくjQueryチックなライブラリ使ってるんだからもっと気の利いたサンプル書けよとか思うところではあるが、ここはぐっとこらえ、あえて手を抜く。なぜなら僕は、めんどくさがり屋だからだ。めんどくさがり屋はめんどくさがることによってお金をもらっている。あーめんどくさい。このブログ長文すぎじゃね?

その後の処理でMySQLに引っこ抜いてきたデータを格納してるわけだが、詳しい使用方法はめんどくさいから省略したいと思う。詳しいO/RマッパーのSequelizeの使い方は公式サイトのUsageを見てよ!

まとめ

っというわけで、node.jsとcheerioを使うことによって、jQueryチックにWebサイトから必要な情報をぶっこ抜くことができるようになったよ!これをもとにクローラを作れば、自動的に必要な情報だけを取ってくることができて、幸せになるはずだ。

ちなみに、速度面については特に検証していない。なぜなら、僕はめんどくさがり屋だからだ。でも、ブラウザシミュレータ型のクローラに比べたら断然早いはずだ。ただし、COOKIEとかが必要になってくると、いろいろめんどくさいことをしなければならないだろうが。

そう、そのめんどくさいことは他のサイトの解説に任せようではないか。

以上、よっしーでした。

関係ないけど、なぜLayerGameの最新の映画版で戸田ちゃんが多部ちゃんにすりかわっているのか。確かにどことなく似てるが、言われなければ分からないぐらいのレベルではない。さすがの僕もそれぐらいの見境はつくよ!

僕の戸田ちゃんを返せ!

LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。

Webサイト制作の実績・料金を見る

この記事のシェア数

どうも僕です!ジェイさんのエンジニア募集のツイートを見て、のこのこと面接にきて採用されたのが僕だ。Twitterは気軽にフォローしてね!「@mas_yda」僕は寂しがってますよ!もう一度言う。僕は寂しがってますよ!関係ないけど、石原さとみっていいよね。

このメンバーの記事をもっと読む