node.jsでスクレイピングしてみる
初めまして。4月入社の山崎です。これが初投稿になります。
今回はnode.jsでスクレイピングをします。
経緯
とあるサイトをリニューアルしていて、旧サイトの静的ページからテキストなどのコンテンツを持ってきて新サイトのHTMLタグの中に貼り付けていくという作業はよくあると思います。
僕もとあるサイトのリニューアル時にそういった作業をやっていたのですが、旧サイトのよくある質問の質問と回答のページが分かれており、回答ページに行ってテキストをとってきてHTMLに貼り付けていました。
ただ、その質問も全部で100個くらいあり、1つ1つコピペするのは面倒くさいので、スクレイピングで特定のURLに一括アクセスし、テキストを引っ張ってきて、新サイトのHTML構造に埋め込み、HTMLを自動生成しようと思ったのがきっかけです。
僕はシェルスクリプトはほぼ書けず普段JSを使うことが多いため、node.jsを使うことにしました。
事前準備
まず、今回使用するライブラリをインストールします。今回の目的はあくまでHTMLを生成するためだけのもので、特にプロジェクトで使うわけではないので、グローバルインストールします。(nodeやnpmなどは事前にインストールされている前提)
$ npm install -g scraperjs
次に、scrape.jsを作成します。
$ touch scrape.js
作成したscrape.jsの先頭で先ほどインストールしたscraperjsを読み込みます。
const scraperjs = require('scraperjs');
requireのパスはそのファイルから一番近いnode_modulesディレクトリのパスを読みます。ですので、これだけではモジュールが見つからないと怒られます。
試しにnodeコマンドでnodeコンソールに入り
$ node
> global.module.paths
とうってみるとrequireがどこからのパスを優先的に読み時むのかが確認できます。そしてグローバルにインストールされたモジュールがどこにインストールされているかというのは
$ npm root -g
コマンドで確認できます。
そして、グローバルのnode_modules
ディレクトリを参照できるようにするには環境変数$NODE_PATH
にグローバルのnode_modules
のパスを追記する必要があります。
.bash_profileに以下を追記してください。
NODE_PATH=`npm root -g`
export NODE_PATH
シェルを再起動し、
$ echo $NODE_PATH
と打ったらグローバルのnode_modules
のパスが環境変数$NODE_PATH
に入っているのがわかります。
ここまで終わったらrequireのエラーが解消して、モジュールが使えるようになったので、いよいよスクレイピング開始です。
HTMLを用意
実在するサイトから引っ張ってくるのもなんかあれなので、自分のローカル環境で適当にHTMLを作ってみました。
まずは、適当なディレクトリにfaqというディレクトリを作成し、スクレイピング対象となるHTMLを用意します。
<!-- 01.html -->
<html lang="ja">
<head>
<title>よくある質問1</title>
<meta charset="utf-8">
</head>
<body>
<h1>お名前は?</h1>
<p>山崎です。</p>
</body>
</html>
<!-- 02.html -->
<html lang="ja">
<head>
<title>よくある質問2</title>
<meta charset="utf-8">
</head>
<body>
<h1>出身は?</h1>
<p>静岡</p>
</body>
</html>
<!-- 03.html -->
<html lang="ja">
<head>
<title>よくある質問3</title>
<meta charset="utf-8">
</head>
<body>
<h1>血液型は?</h1>
<p>A型</p>
</body>
</html>
次にPHPのビルドインサーバーでWEBサーバーを起動します。
$ php -S 127.0.0.1:8000 -t ./faq
テキストを引っ張ってきて、新しくHTMLを生成する
旧サイトのHTMLを 新しくこんな感じの構造に書き換えたいとします。
<dl>
<dt>質問1</dt>
<dd>質問1答え</dd>
<dt>質問2</dt>
<dd>質問2答え</dd>
<dt>質問3</dt>
<dd>質問3答え</dd>
</dl>
やっていきます。
URLが連番になっているのでページの分だけ処理を繰り返せるよう、ループを回します。
URLを指定してscraperを生成しscrapeメソッドの引数に成功時の処理を書いていきます。
変数question
にh1のテキストを入れて、変数answer
にpのテキストを入れます。
jQueryオブジェクトがコールバックの引数として渡ってくるので、これよりも複雑な構造になっていたとしても普段jQueryを使ってる人にとっては割と簡単に情報が取得できると思います。
const scraperjs = require('scraperjs');
for (let i = 1; i {
let question = $('h1').text();
let answer = $('p').text();
let html = `<dt>${question}</dt>n<dd>${answer}</dd>n`;
console.log(html);
}).catch((error) => {
console.error('Error:', error);
});
}
ES6のテンプレートリテラルで生成するHTMLの雛形を作成します。今までテンプレートリテラルはあまり使ったことなかったのですが、改行コードなどもそのまま書けるし読みやすいのでいいですね。
let html = `<dt>${question}</dt>n<dd>${answer}</dd>n`;
そして生成したHTMLを出力します。
console.log(html);
出力先のHTMLファイルを作成し、ターミナルでscrape.jsを実行します。出力先を先ほど作ったHTMLファイルに指定します。
$ touch ./new.html
$ node scrape.js > new.html
実行結果
<!-- new.html -->
<dt>お名前は?</dt>
<dd>山崎です。</dd>
<dt>出身は?</dt>
<dd>静岡</dd>
<dt>血液型は?</dt>
<dd>A型</dd>
出力できました。あとは生成したHTMLをまるっとコピーしてdlの中に貼り付ければおしまいです。
まとめ
今回のケースの場合は、量が少なかったのでここまでやるぐらいだったら、コピペした方が早いと思いますが、量次第ではかなり時間がかかってきますし、確認作業や修正も大変だと思うので、こういう機会があったら、とても簡単なので試してみてください。
最後まで読んでいただいた方ありがとうございました。
Source: SEO