JavaScript
Node.js
More than 5 years have passed since last update.

Qiita初投稿です。初めまして、yosuke_furukawaと申します。

Node.jsでスクレイピングというとjsdomなんかが有名ですよね。

以下のやり方が参考になると思います。
node.jsとjQueryでスクレイピングするウェブアプリの作り方 | さくらたんどっとびーず

でも実際に外部サイトをスクレイピングしようとすると、requestモジュール使うかsuperagentモジュール使うかしなきゃいけなくて若干面倒です。これがScraperを使えばもう少し簡潔に記述できます。

Scraperの強力さを試すために東京Node学園祭で出た問題を紹介します。

Node.jsを使って、リクルートテクノロージズ社のサイトをスクレイピングしてください。
"recruit"もしくは"リクルート"の文字が何文字あるかワード件数を求めよ。
ただし、aタグのものは除去してカウントすること。

これ、Scraperを使えば簡単に回答できます。以下のようなスクリプトになります。
(ただし、本当はこの問題は当日の14時までに提出する必要があったのですが、すっかり見落としていて、時間内に回答できなかったので合っているかどうかは保証できませんが。。。)

var scraper = require('scraper');
scraper('http://recruit-tech.co.jp/', function(err, $) {
  if (err) {throw err;}
  $('a').remove();
  var ans = $('html').html().match(/リクルート|recruit/ig).length;
  console.log(ans);
});

上記のスクリプトを実行すると 2012/12/08 現在で 33 という回答が返ってきます。

これだけでも簡潔に記述できる事がわかってもらえると思いますが、Scraperにはさらに以下の様な機能が備わっています。

  • 複数ページアクセス機能
  • ユーザーエージェント指定機能
  • リクエストタイミング指定機能

複数ページアクセス機能

以下のように配列形式でURLを指定することで複数ページに指定することが可能です。

var scraper = require('scraper');
scraper([
        'http://qiita.com/advent-calendar/2012/javascript-mechanco-tips',
        'http://qiita.com/advent-calendar/2012/ruby',
        'http://qiita.com/advent-calendar/2012/vm'
], function(err, $) {
// ….
});

ユーザーエージェント指定機能

URLを渡すだけじゃなく、ユーザーエージェントを指定することが可能です。以下のようにします。

var scraper = require('scraper');
scraper(
    {
       'uri': 'http://search.twitter.com/search?q=nodejs',
       'headers': {
           'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'
        }
    }, function(err, $) {
        if (err) {throw err}
        $('.msg').each(function() {
            console.log($(this).text().trim()+'\n');
        });
    }
);

headersオブジェクトにユーザーエージェントを指定すればOK。

リクエストタイミング指定機能

複数ページから一気に同じサイトのページにアクセスするとDoS攻撃とみなされてしまう可能性もあります。
その時のためにsetTimeoutとかでタイミングを調整する必要があるのですが、そのタイミングをライブラリ側で指定できるという機能です。

var scraper = require('scraper');
scraper(
    [
        'http://search.twitter.com/search?q=javascript'
        , 'http://search.twitter.com/search?q=css'
        , 'http://search.twitter.com/search?q=html5'
    ], function(err, $) {
        if (err) {throw err;}
        $('.msg').each(function() {
            console.log($(this).text().trim()+'\n');
        });
    }, {
        'reqPerSec': 0.2 // 5秒間隔でページにアクセスしに行きます。1000/reqPerSecをして、そのミリ秒の間、setTimeoutで待つようです。
    }
);

欠点

こんな風に簡潔に記載できて色々機能があるscraperですが、大変残念なことに2年前で開発が止まっていて、以下の様な欠点があります。

  • jQueryのバージョンが v1.6と多少古い。 v1.8とかの新機能は使えません。
  • デフォルトのユーザーエージェントが'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'になっており、MacユーザーやChromeユーザーからすると自分たちのブラウザで見ているものと違うものが返ってきている可能性があります。

ユーザーエージェントを指定するのは多少面倒なので簡易的に指定できる方法があるとより便利なのですが。この辺りの欠点についてはセルフアップデートするしか今のところは無さそうです。機会があればPullRequest投げてみます。

Qiitaアドベントカレンダーをスクレイピング

最後にこれまでのQiitaアドベントカレンダーをスクレイピングして一覧を作ってみました。以下のスクリプトで取得できます。

var scraper = require('scraper');
scraper('http://qiita.com/advent-calendar', function(err, $) {
  var cal_links = [];
  $('section li h2 a').each(function(){
    var cal_link = 'http://qiita.com' + $(this).attr('href');
    cal_links.push(cal_link);
  });
  scraper(cal_links, function(err, $) {
    if (err) {throw err;}
    var title = $('#adcal-title').text().trim();
    console.log();
    console.log('#'+title);
    var i = 0;
    $('.user a').each(function() {
      var username = $(this).attr('href').replace(/users|@github|href|\//g, '');
      var itemTitle = $('.right:eq('+i+') :not(div)').html().trim();
      var comment = $('.right:eq('+i+') .comment a').attr('href');
      var link = $('.right:eq('+i+') a').attr('href');
      if (link.indexOf('item') < 0) {
        link = comment;
      } else {
        link = 'http://qiita.com'+link;
      }
      if (link && link != '') {
        itemTitle = itemTitle && itemTitle !=  '' ? itemTitle : link;
        console.log('- %sさん:[%s](%s)', username, itemTitle, link);
      }
      i++;
    });
  });
});

実行すると以下のようになります。

伺か Advent Calendar 2012

Ruby Advent Calendar 2012

XOOPS Themes and Templates Advent Calendar 2012

Google Apps Script Advent Calendar 2012

VM Advent Calendar 2012

Objective-C Advent Calendar 2012

Machine Learning Advent Calendar 2012

zsh Advent Calendar 2012

Play or Scala Advent Calendar 2012

ドキュメント作成Tips Advent Calendar 2012

Lisp Reader Macro Advent Calendar 2012

Corona SDK Advent Calendar 2012

Emacs Advent Calendar 2012

Perl6 Advent Calendar 2012

zsh Advent Calendar 2012

ドキュメント作成Tips Advent Calendar 2012

JBoss Advent Calendar 2012

iOS Advent Calendar 2012 / Aug.

あまり知られていないけど役に立つJavascript tips Advent Calendar 2012

Play or Scala Advent Calendar 2012

Corona SDK Advent Calendar 2012

D言語 Advent Calendar 2012

Git Advent Calendar 2012 / Jun.

Emacs Advent Calendar 2012

Ruby on Rails Advent Calendar 2012 / Sep.

Git Advent Calendar 2012

Git Advent Calendar 2012 / Jun.

Ruby開発環境 Advent Calendar 2012 / Jul.

Backbone.js Advent Calendar 2011

iOS Advent Calendar 2012 / Aug.