102
107

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Day 8

Node.jsのScraperでお手軽スクレイピング!

Last updated at Posted at 2012-12-09

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.

102
107
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
102
107

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?