プログラマでありたい

おっさんになっても、プログラマでありつづけたい

AWS Lambda+PhantomJS/CasperJSでスクレイピング

 AWS Lambdaはサーバ不要のプログラムの実行エンジンです。インフラ側はAWSが管理するのですが、実行原理が解っていると思いの外に自由度が高いです。Shellやコマンドを同梱させれば動くし、依存するライブラリがなければAmazon Linux上でビルドして同梱させれば良いのです。その実例の1つに、Lambda+PhantomJSがあります。PhantomJSは、ブラウザ不要で画面描画ができるツールです。リンクのクリックやボタンの押下・フォームに入力も出来るので、ログインが必要なページの情報を取ってくるということやスクリーンショットを取るといったことも可能です。LambdaとPhantomJSを組み合わせてお手軽スクレイピングをしてみましょう。

プロジェクトの用意



 Github上にビルド済みのライブラリ込のプロジェクト(phantom-lambda-template)があるので、それを使います。生のライブラリ等が配置されているので、こういう感じでやるのだと参考にもなると思います。

github.com

ソースを落としてきたら、まずそのディレクトリに移動して依存関係のモジュールをインストールします。

$ npm install

次に環境設定ファイルを作ります。

$ npm run setup 
$ vi .env
AWS_ENVIRONMENT=development
AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXX
AWS_ROLE_ARN=arn:aws:iam::123456789012:role/lambda-phantomjs
AWS_REGION=us-east-1
AWS_FUNCTION_NAME=phantom-lambda-template
AWS_HANDLER=index.handler
AWS_MODE=event
AWS_MEMORY_SIZE=128
AWS_TIMEOUT=300
AWS_DESCRIPTION=
AWS_RUNTIME=nodejs

AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_ROLE_ARNの部分を編集しておいてください。AWS_TIMEOUTもデフォルト3秒なので長めに設定しておきます。(この例は、最大値である300秒にしています。)注意点としては、このACCESS_KEYが設定されているユーザは、Lambdaのアップロードする権限とローカルで実行する為の権限です。ROLE_ARNの方が、実際にLambdaで実行される場合の権限になります。それぞれ適切なものを設定しておく必要があります。

ここまで設定したらローカル実行してください。エラーがでなければ、O.K.です。

$ npm run start

次にLambdaにアップロードします。下記のコマンドでアップできるようになっています。素晴らしい

$ npm run deploy

AWSコンソールにログインしてみるとLambdaファンクションが出来ているはずです。
f:id:dkfj:20151201015934p:plain
実行してみてください。デフォルトのサンプルだと、コンソールログに'Hello from phantom'と出るだけです。

このテンプレートがやっていること



 ちなみにこのモジュールをどうやって作っているのかは、同梱のシェルを見ると解ります。Amazon Linuxのインスタンスでビルドしたモジュールを利用しています。PhantomJSに限らず、同じようなやり方で色々なモジュールをLambdaで使えます。

build-phantomjs.sh

# Launch an EC2 instance using the region-appropriate AMI from this document:
# http://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.

# Use a fairly beefy Spot Instance (c4.large is good) unless you want to wait
# for a long time. Don't bother with a t2.micro.

# Once it starts, SSH into the machine:

ssh ec2-user@your-ec2-box.amazonaws.com

# And then run (following the instructions from
# http://phantomjs.org/build.html):

sudo yum -y install gcc gcc-c++ make flex bison gperf ruby \
  openssl-devel freetype-devel fontconfig-devel libicu-devel sqlite-devel \
  libpng-devel libjpeg-devel

wget https://github.com/ariya/phantomjs/archive/1.9.7.zip
unzip 1.9.7.zip
cd phantomjs-1.9.7/
./build.sh --confirm

# Once it's finished, retrieve the build product (run from your local machine):
scp ec2-user@your-ec2-box.amazonaws.com:phantomjs-1.9.7/bin/phantomjs .

# Since this AMI includes a newer version of libicudata than Amazon Lambda,
# we need to include those libraries too.
# This won't be necessary once
# https://github.com/ariya/phantomjs/issues/12948 is fixed.
scp ec2-user@your-ec2-box.amazonaws.com:"/usr/lib64/libicu*50" .

# That's it! Don't forget to terminate the EC2 instance.

カスタマイズ



 スクリーンショットを取る例に変えてみます。

var page = require('webpage').create();

page.viewportSize = { width: 1920, height: 1080 };
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36';

page.open('http://github.com/', function() {
  page.render('github.png');
  phantom.exit();
});

ローカルで実行すると、github.pngというファイルが出来ると思います。後は、これをS3等にアップロードするようなものを使うと、Lambdaクローラーとして機能し始めます。この辺りはハマりポイント多いので、また時間作って書き足します。

まとめ



 Lambdaは、今年のアップデートで実行時間がMax5分。更にスケジュール実行の機能が付きました。これでクローラー作る為にサーバを用意する必要がほぼなくなっています。あとは、Pythonで作るか、Node.JSで作るかです。今年はNode.JSの良いクローラー本が出たので、それを参考にどんどんアイデアを実現できればなと思います。Ruby本もよろしくね

See Also:
クローラー/スクレイピング本がざくざく
『Rubyによるクローラー開発技法』を書きました
Rubyによるクローラー開発技法の目次
Lambdaで作るクローラー/スクレイピング


参照:
クローラー/Webスクレイピング Advent Calendar 2015
プログラマになりたい Advent Calendar 2015

Capybara+PhantomJS+Nokogiriを利用してスクレイピング

CapybaraとPhantomJS、Nokogiriを利用してのクローラー・スクレイピングの紹介です。

PhantomJSとは?



f:id:dkfj:20150214123247p:plain
 PhantomJSは、ヘッドレスブラウザと呼ばれるWebKitのエミュレータです。ヘッドレスブラウザとは、GUIではなくCUIから利用できるブラウザでプログラムから呼ばれます。UIのテストツールとしてSeleniumのようなサービスがあります。Seleniumはブラウザを直接操作するので、環境依存や動作が重いといった幾つかの問題点があります。そこでよく利用されるのがPhantomJSです。Seleniumに比べて、軽量というメリットがあります。RubyからPhantomJSを扱うライブラリとして、Poltergeistがあります。

Capybaraとは?



 Capybaraは、WebシステムのUI層のテストをサポートするためのライブラリです。主にDSL機能とDriver機能があり、テストフレームワークやブラウザ&ブラウザシミュレータを透過的に扱うことができます。
f:id:dkfj:20150214123816p:plain

Nokogiriとは?



f:id:dkfj:20140415021125p:plain
 Nokogiriは、Rubyで実装されたHTML/XMLの構文解析器(パーサー)です。Rubyの構文解析器としてはデファクト・スタンダードで、スクレイピングする際の必須のツールとなっています。Rubyで実装されたクローラー・スクレイピングライブラリの大部分は、内部的にNokogiriを利用しています。使い方の詳細は、下記のリンクを参照していただければと思います。
Ruby製の構文解析ツール、Nokogiriの使い方 with Xpath

PhantomJSとCapybaraのインストール



 PhantomJSのインストールは、Windowsでは少し面倒くさいことが多いです。PhantomJSのサイトからダウンロードしてパスを通すことが必要です。Mac+HomeBrewであれば、下記のコマンドでインストールできると思います。

brew install phantomjs

 その後に、PoltergeistやCapybaraをインストールします。

gem install nokogiri
gem install poltergeist
gem install capybara

 Windowsのnokogiriのインストールはハマる確率が高いです。ビルド済みのgemをダウンロードする方をお勧めします。x86-mingw32(32ビット版)もしくは x64-mingw32(64ビット版)と書かれているものがWindowsようです。
※最近、64ビット版も提供されるようになったようですね。

スクレイピング



 下記がCapybaraとPhantomjsとNokogiriを使ったサンプルスクリプトです。

require 'capybara'
require 'capybara/dsl'
require 'capybara/poltergeist'

class Scrape
  #DSLのスコープを別けないと警告がでます
  include Capybara::DSL

  def initialize()
    Capybara.register_driver :poltergeist_debug do |app|
      Capybara::Poltergeist::Driver.new(app, :inspector => true)
    end

    Capybara.default_driver = :poltergeist
    Capybara.javascript_driver = :poltergeist
  end

  def visit_site
    page.driver.headers # => {}
    #ユーザエージェントの設定(必要に応じて)
    page.driver.headers = { "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36" }
    #リファラーの偽装(特に不要)
    #page.driver.add_headers("Referer" => "http://yahoo.co.jp")
    page.driver.headers
    visit('http://www.yahoo.co.jp')
    #スクリーンショットで保存
    page.save_screenshot('screenshot.png', :full => true)
    #within(:xpath, "//*[@id='toipcsfb']/div[1]/ul[1]") do
    #Nokogirオブジェクトの作成
    doc = Nokogiri::HTML.parse(page.html)
    puts doc.title
  end
end

scrape = Scrape.new
scrape.visit_site

ポイントとしては、次のとおりです。
・Capybara DSLのスコープを別ける
  しないと、次のように警告でますよ
  including Capybara::DSL in the global scope is not recommended!
・Nokogiriを使ってる
  Capybaraの記法に慣れていない場合は、Nokogiriを作ってしまうのも1つの手です。
  無駄が多いけど

このサンプルでは、面倒くさいのでJavaScriptによって動的に構築された部分とってませんが。。。

まとめ



 JavaScriptバリバリのサイトからスクレイピングしたい場合、PhantomJSはお勧めです。少し重いものの、Seleniumでブラウザを動かすより断然軽量です。またブラウザのインストールが不要なので、サーバサイドで動かすことも容易です。今、PhantomJSをAWS Lambdaで動かそうかなと試しています。これが出来ればかなり面白いことが出来そうですね。
 この辺りの話をまとめた、「Rubyによるクローラー開発技法」という本を出しています。ひたすらクローリングとスクレイピングしているので、何か参考になることがあればと思います。


See Also:
『Rubyによるクローラー開発技法』を書きました
RubyでWebスクレイピングの話をしてきました。第1回Webスクレイピング勉強会@東京
Ruby製の構文解析ツール、Nokogiriの使い方 with Xpath
あらためてRuby製のクローラー、"anemone"を調べてみた
オープンソースのRubyのWebクローラー"Anemone"を使ってみる

Excel VBA+IEでのスクレイピング

 書くと宣言しながら、すっぽかしていたクローラー/スクレイピング Advent Calendar 2014の11日目です。ExcelのVBAで、IEの操作が出来ます。またその中の機能にDOM操作があります。この二つがあれば、Excelだけでスクレイピングできますよという話です。
 何を隠そう、私が初めてスクレイピングに手を染めたのはこのExcel VBAスクレイピングでした。毎月定期的に所定のデータを観測するという仕事があって、面倒くさくて仕方がない私は記録用のExcelを魔改造して勝手に取得するようにしました。1時間くらい掛かってた作業も、ボタンポチになったので随分楽になったと記憶しています。(結局、ボタンを押すのも面倒くさくなって、毎日サーバサイドで動かすようにしましたが)
 スクレイピングの手始めにピッタリのExcel VBAスクレイピングです。環境の用意が簡単なので、ノンプログラマーでも何とかなるのではとおもいます。

Excel VBA+IEスクレイピングでの主要な機能



 Excel VBAでスクレイピングする場合は、下記のクラス・メソッドを利用します。一部バージョンによってサポートしていないので、ご注意ください。

  1. InternetExplorerクラス

 名前の通りInternetExplorerを操作するクラス。Excelから実行で、IEがポコっと立ち上がる

  1. HTMLElementCollection

 InternetExplorerクラスのドキュメントを扱うプロパティ?

  1. getElementsByTagName

 タグ名からエレメント・コレクションを取得するメソッド

  1. getElementsByClassName

 クラス名からエレメント・コレクションを取得するメソッド
 (XP等では、未サポート?)

  1. getElementById

 ID名からエレメントを取得するメソッド
 (XP等では、未サポート?)

Excel VBA+IEスクレイピングの下準備とサンプルソース



次のライブラリを参照設定します。参照設定、懐かしいなぁ。

  • Microsoft HTML Object library
  • Microsoft Internet Controls

f:id:dkfj:20141227172806p:plain

サンプルソース
手抜きですが、タグ名で取得しています。

Option Explicit
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private objIE As InternetExplorer

Sub main()
    Set objIE = New InternetExplorer
    Dim url
    url = "http://www.hatena.ne.jp"
    objIE.Visible = True
    objIE.Navigate2 (url)
    
    '読み込み完了待ち
    While objIE.readyState <> READYSTATE_COMPLETE Or objIE.Busy = True
        DoEvents
        Sleep 100
    Wend
    Sleep 100
    
    Dim objDoc As HTMLElementCollection
    Set objDoc = objIE.document
    Dim element As IHTMLElement
    For Each element In objDoc.getElementsByTagName("a")
            MsgBox (element.innerText)
    Next element
    
    objIE.Quit
    Set objIE = Nothing
End Sub

 Excel VBAなので、取得結果をExcelのシートに格納するなどに、絶大な威力を発揮します。Excel VBAなどで検索すると色々でてくるので、ぜひ見てください。

まとめ



 自分としては、もはやExcel VBAでスクレイピングすることはないと思います。しかし、プログラムの実行環境がないよという場合には、Excel VBAという選択肢も良いのではないでしょうか?世の中には、VBAを極めている人は多数いるので、身近な人に聞けるというメリットもあります。ノンプログラマでも、ちょっと頑張れば何とかなると思うので、挑戦してみてはいかがでしょうか?
 また、スクレイピングやクローラーの作成手順を一から知りたい人は、Rubyだけどこの本読むとよいですよ。

See Also:
「データを集める技術」という本を執筆しました
非エンジニアでも何とか出来るクローラー/Webスクレイピング術
プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」
『Rubyによるクローラー開発技法』を書きました

参照:
クローラー/スクレイピング Advent Calendar 2014
IE自動操作コード一覧(ExcelVBA / VBScript) - Qiita

データを集める技術 最速で作るスクレイピング&クローラー (Informatics&IDEA)

データを集める技術 最速で作るスクレイピング&クローラー (Informatics&IDEA)

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Lambdaで作るクローラー/スクレイピング

 話題のAWS Lambda Advent Calendar 2014の14日目です。クロスポストで、クローラー/スクレイピング Advent Calendar 2014の14日目でもあります。

 re:Inventで発表されて以来、注目のLambdaです。サーバを用意しなくても、バッチを直接実行できるとあって、ユースケースを考えるだけで夢が広がります。今年はクローラー本を出したこともあって、Lambdaで作るクローラー/スクレイピングをテーマにします。

クローラー/スクレイピングとは?



 Webクローラーは、Webサイトを巡回してデータを取得するプログラムです。スクレイピングは、取得したデータから目的の情報を抜き出すことを指します。一般的には、クローラーの中に、スクレイピングの機能を包含していることが多いです。また、特定のページだけ取得してデータを抜き出すことを、スクレイピングと呼ぶことが多いです。

クローラーの機能



 クローラーの機能は、ざっくり分類すると下記の4つです。
今回は、2と3の部分をLambdaで実施します。

1. 巡回先を決定する
2. ダウンロード
3. 情報を抜き出す
4. (データを保存する)

Lambdaクローラーのモデル



 Lambdaクローラーのモデルとしては、次のような形になります。
f:id:dkfj:20141214045906p:plain

 LambdaCrowlerで、指定されたURLをNode.jsのhttpモジュールを使ってダウンロードします。ダウンロードをしたファイルをS3に保存します。保存先のS3にEventの設定をしておき、parseHtmlのLambdaファンクションを呼び出すようにします。呼び出されたparseHtmlは、イベント情報から呼び元のs3ファイルを取得します。ファイル取得後に、cheerioというスクレイピング用のモジュールを利用して所定の情報を抜き出します。今回は、Yahoo!Financeから株価情報を取得する例とします。

Lambdaクローラーの実装



 まずLambdaCrowlerの実装です。諸般の事情により、取得先のURLと保存するファイル名はリテラルで記述しています。

console.log('Loading event');
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var http = require ('http');

exports.handler = function(event, context) {
    var bucket = 'lambda-crawler';
    var key = 'test';
    var body;
    http.get("http://stocks.finance.yahoo.co.jp/stocks/history/?code=9984.T", function(res) {
        console.log("Got response: " + res.statusCode);
        res.on("data", function(chunk) {
            console.log('chunk: ');
            body += chunk;
        });

        res.on('end', function(res) {
            console.log('end')
            putObject(context, bucket, key ,body);
        });
    }).on('error', function(e) {
        context.done('error', e);
    });

    function putObject(context, bucket, key ,body) {
        var params = {Bucket: bucket, Key: key, Body: body};
        console.log('s3 putObject' + params);

        s3.putObject(params, function(err, data) {
            if (err) {
                console.log('put error' + err);  
                context.done(null, err);
            } else {
                console.log("Successfully uploaded data to myBucket/myKey");
                context.done(null, "done");
            }
        });
    }
};

 注意点としては、http.requestがcallbackを利用している点です。リクエストの終了イベントを拾い、その後にS3に保存するように記述しています。実行すると次のようなログがでます。

START RequestId: 2b379992-8300-11e4-88d6-37d91d3def55
2014-12-13T19:42:26.841Z	2b379992-8300-11e4-88d6-37d91d3def55	Got response: 200
2014-12-13T19:42:26.842Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:26.842Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.022Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.022Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.203Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	chunk: 
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	end
2014-12-13T19:42:27.384Z	2b379992-8300-11e4-88d6-37d91d3def55	s3 putObject[object Object]
2014-12-13T19:42:27.574Z	2b379992-8300-11e4-88d6-37d91d3def55	Successfully uploaded data to myBucket/myKey
END RequestId: 2b379992-8300-11e4-88d6-37d91d3def55
REPORT RequestId: 2b379992-8300-11e4-88d6-37d91d3def55	Duration: 2558.28 ms	Billed Duration: 2600 ms 	Memory Size: 128 MB	Max Memory Used: 18 MB	

 次にparseHtmlの実装です。非標準のモジュールであるcheerioを利用しています。

console.log('Loading event');
var cheerio = require('cheerio');
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var http = require ('http');

exports.handler = function(event, context) {
    var record = event.Records[0];
    var bucket = record.s3.bucket.name;
    var key = record.s3.object.key;
    console.log('record:' + record);
    console.log('bucket:' + bucket);
    console.log('key:' + key);
    getObject(context, bucket, key)
    function getObject(context, bucket, key) {
        console.log('s3 getObject');
        s3.getObject({Bucket:bucket, Key:key},
            function(err,data) {
                if (err) {
                    console.log('error getting object ' + key + ' from bucket ' + bucket +
                       '. Make sure they exist and your bucket is in the same region as this function.');
                    context.done('error','error getting file'+err);
                }
                else {
                    var contentEncoding = data.ContentEncoding;
                    var contentBody = data.Body.toString(contentEncoding);
                    parseHtml(context, contentBody); 
                }
            }
        );
    }
    function parseHtml(context, body) {
        console.log('parseHtml');
        var $ = cheerio.load(body);
        var title = $("title").text();
        var stockPrice = $('td[class=stoksPrice]').text();
        console.log('stockPrice:'+stockPrice);
    }	
};

 まずイベントからバケット名とファイル名を取得します。その情報を元に、S3からファイルを取得します。ファイルを取得したら、cheerioでスクレイピングです。cheerioの使い方は割愛しますが、タグ名+クラス名で、要素を指定しています。
 尚、非標準のモジュールを利用しているので、ソースと一緒にモジュールもzipで固めてアップロードする必要があります。

├── node_modules
│     ├── aws-sdk
│     └── cheerio
├parseHtml.js

 上記のような構造のディレクトリをまとめてzip化します。

zip -r code.zip parseHtml.js node_modules/

アップロードしたファイルの中で、どのファイルを実行するかは「File name」で指定します。
f:id:dkfj:20141214051542p:plain

実行すると、次のようなログが出てきます。stockPriceの部分が、今回取得しようとした情報です。

START RequestId: 1d834bf2-8300-11e4-800c-bd34141de807
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	record:[object Object]
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	bucket:lambda-crawler
2014-12-13T19:42:05.492Z	1d834bf2-8300-11e4-800c-bd34141de807	key:test
2014-12-13T19:42:05.493Z	1d834bf2-8300-11e4-800c-bd34141de807	s3 getObject
2014-12-13T19:42:06.174Z	1d834bf2-8300-11e4-800c-bd34141de807	parseHtml
2014-12-13T19:42:07.173Z	1d834bf2-8300-11e4-800c-bd34141de807	stockPrice:7,336
Process exited before completing request

END RequestId: 1d834bf2-8300-11e4-800c-bd34141de807
REPORT RequestId: 1d834bf2-8300-11e4-800c-bd34141de807	Duration: 1920.75 ms	Billed Duration: 2000 ms 	Memory Size: 128 MB	Max Memory Used: 27 MB	

ハマリポイント



 成果物としてのソースは非常に簡単ですが、かなりハマりました。ハマったポイントは、以下の通りです。

  1. そもそもNode.js知らん
  2. S3 Event Notificationのロール登録時に、ロール中にprincipalのserviceにs3.amazonaws.comが必要

課題



 一応完成しましたが、1つ課題があります。最初のLambdaファンクションを呼び出すのを誰にするかです。呼び出し方として、s3 event notification,Kinesis stream, DyanmoDB stream、もしくはプログラムから呼び出すなどの手段あります。そもそも今回Lambdaでクローラーを作ろうと思った目的は、サーバレスで作りたかったからです。どの呼び出し元を選んだとしても、イベント起こす為の起点が必要です。その辺りどうするかがポイントになりますね。
 クローラーという用途で考えると、何らかの方法で取得対象をDynamoDBに登録して、Lambdaを呼び出して結果を記載するという方法があっているかもしれません。もしくは、呼び元の小さなインスタンスを用意して、処理自体をLambdaの潤沢なリソースを使うというのも考えられます。いずれにせよ、Lambdaを使うには、信頼性の高いスケジュール・ジョブの実行主体が必要になります。この辺りもサービス化して貰えるとありがたいですね。

まとめ



 駆け足でしたが、やはりLambdaは魅力的なサービスです。クローラーを作ってると、IPアドレスとリソースの確保が大変です。もう少し色々考察してみたいと思います。ちなみに、Rubyによるクローラー開発技法という本を書いたのですが、リクエストの中で多いのがPython版とNode.js版を出してとのことです。Lambdaが出たことなので、誰かNode.js版書きませんか?

See Also:
KimonoLabsと今後のサービスのあり方のはなし
プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」
『Rubyによるクローラー開発技法』を書きました


参照:
AWS Lambda Advent Calendar 2014
クローラー/スクレイピング Advent Calendar 2014
全部俺Advent Calendar

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

S3のイベント通知機能(S3 Event Notifications)に対するユースケースを考える

 日本のAWS関係者がre:Inventの発表で盛り上がっているなか、風邪ひいて寝込んでました。季節の変わり目には、気をつけましょう。
 さて、そんな感じで全然追いつけていませんが、特に面白いなぁと思ったのが、S3 Event NotificationsとAWS Lambdaです。Lambdaについてはまだ触っていないので何ともいえない部分がありますが、発表内容を見る限りインフラの在り方を変えるような存在かもしれません。そして、S3のEvent Notifications。これ、めちゃくちゃ便利です。S3のイベント(現在は、putとpushとcopy)が発生したら、SNSやSQSに通知を送れるという機能です。何が便利かというと、ファイルのアップロードをトリガーに処理を書けるということです。頭に浮かんだユースケースを1つ書いてみます。

S3 Event Notificationsを使ったクローラーのユースケース



 最近、クローラー開発のステマばかりしているので、今回もそのケースで説明してみます。処理の流れとしては、下記のようなものを想定します。

  1. データをダウンロードする
  2. ダウンロードしたデータを生データとしてS3に保存する
  3. S3の未処理の生データを取得して、加工処理をおこなう

 この処理の中で、3の部分が少し曲者になります。未処理のデータが何かというのを特定する方法が必要になるからです。今までの対処方法としては、次のような方法が考えられます。

  • 処理前/済みのデータを管理するデータベース等を用意する
  • 処理前と処理済みでディレクトリをわけて、処理が終わったら移動させる
  • 2の保存と同時に、SQSに登録する

 一手間があって面倒くさいというのが実情でした。また、処理サーバを複数にすると排他制御の仕組みが必要になり、複雑になります。3の処理が一番楽なのですが、S3に直接データだけアップしたいというようなケースには対応できません。

 S3 Event Notificationsを使えばどうなるのでしょうか?
下に簡単な図を載せていますが、クローラーはただデータを集めて保存するだけに徹すればよいことになります。その後のS3⇒SQS⇒(EC2上での)処理は、良きに計らえという状態になります。疎結合なアーキテクチャになりますよね。また処理サーバをキューの数に応じたAutoScaleにしておいたら、処理が溜まったら自動的に増強してくれます。

f:id:dkfj:20141115131937p:plain

S3 Event Notificationsをユースケースの妄想



 この他にも、いろいろな使い道が考えられます。みなさん、いろいろ妄想してみてください。

  • ElasticTranscodeと連携して、アップロードしたら動画変換
  • サーバーレスで、SNSを発行するトリガー
  • (Getイベントに対応したら)メッセージを読んだら自動的に削除
  • (Getイベントに対応したら)メッセージを読んだら自動的に爆発

S3 Event Notificationsの使い方



 未来有望なS3 Event Notificationsです。妄想するだけでなく、実際に使ってみましょう。通知を送るだけであればAWSマネージメントコンソールだけで簡潔できます。

手順としては、次の通りです。

  1. SQS(SNS)を作成し、パーミッションの設定をする
  2. 新規もしくは既存のS3バケットに、Event設定をする
  3. ファイルをアップロードする
  4. SQS(SNS)を確認する

 まず1つ目ですが、今回はSQSの例を紹介します。ポイントは、パーミッション設定です。
対象のバケットからの通知を許可します。具体的には、SourceARNでStringLikeの条件をつけます。

f:id:dkfj:20141115133107p:plain

 2つ目のS3バケットへの設定は、簡単です。
バケットのPropertyの、Eventの項目を設定します。

f:id:dkfj:20141115133258p:plain

 先ほどの権限設定がうまくいっていないと、下記のようなエラーがでます。

Unable to validate the following destination configurations :Permissions on the destination queue to not allow S3 to push notifications from this bucket

 ファイルをアップロードすると、設定が上手くいっていればSQSの画面からキューが登録されているのを確認できます。
f:id:dkfj:20141115133536p:plain
f:id:dkfj:20141115133604p:plain

感想



 とっても便利です。システムの肝は、疎結合&シンプルだと思います。それを実現するために、さらに便利な機能が追加されたと言えるでしょう。更にAWS Lambdaと組み合わせれば、いろいろな事が出来そうです。楽しみ!!

クローラーとAWSが出会ったら?第3回Webスクレイピング勉強会@東京

 2014/10/26に開催された第3回Webスクレイピング勉強会@東京に参加して、発表してきました。今回は、スクレイピングと少し離れてAWSを使ってクローリングするという話です。クローラー/スクレイピングとAWSは相性が良いというのは、昔から思っていたのでテーマとして扱うことは早めに決めていました。しかし、話の構成を、具体的なテクニックの話にするか、概念的な話にするか、少し悩みました。なるべき多くの人に伝わるように、概念的な話をしたつもりです。具体的な部分についてはRubyによるクローラー開発技法を読んで頂ければと思いますw

発表資料



Scraping withawsAWSを利用してスクレイピングの悩みを解決するチップス

 資料の構成としては、クローリングする際の悩みをあげた上で、AWSを使えばどう解決できるのかという構成にしています。AWSのサービスの簡単な紹介と、クローラーを作成する上で便利なサービスを3つ挙げています。EC2とS3,SQSです。前の2つのサービスについては納得すると思いますが、SQSについては何故と思う方もいるかもしれません。その辺りを構成含めて、簡単に紹介しています。
 

他の方の発表資料



 今回は、オープニングを含めて7人の発表がありました。どれも面白く、参考になりました。

Webスクレイピング勉強会@東京 オープニングトーク (第3版) #東京スクラッパー


クローリングしにくいものに挑戦 公開用
 @luminさんは、本職でスクレイピングしているプロとあって非常に濃い内容でした。スクレイピングの上級編は、プロトコル解析から始まるとのことです。なかなか到達できない世界ですね。


ソーシャル・スクレイピング(2014年10月Webスクレイピング勉強会資料)
 @YuzoAkakuraさんは、マスコミで働いているシステムエンジニアとのことです。データジャーナリズムという未知の分野を垣間見せて頂けました。データジャーナリズムとは、「データからニュースを発見し、わかりやすく伝える手段」とのことです。凄く合点がいきました。


第3回Webスクレイピング勉強会@東京 happyou.info

 @shogookamotoさんは、自作のスクレイピングサービスの紹介でした。上場企業や政府機関をスクレイピングしてRSSで配信するという素敵なサービスです。紹介としては1行でさらっと言えるないようですが、取得対象を解析して、それぞれに対応するというのは並大抵の労力ではなかったと思います。凄いサービスです。

20141022 リサーチ向け・ブラウザだけでスクレイピング(浅野)
 @hirosuke_asanoさんは、コピペで出来るスクレイピングを紹介しています。これも規模によっては、よくやります。スクレイピングは手段にすぎないので、用途や個人のスキルに応じて、適切なものを選べばよいのですよね。

実践Excelスクレイピング
 @h_sinoharaはネタっぽく語られていましたが、非常に共感がいく内容でした。Perlで苦労した話とか、Excelでスクレイピングするとか、いろいろ通った道ですね。特にExcelスクレイピングについては、もっと多くの人に知って貰えればと思います。web::queryは必殺技

Togetter



 Togetterのまとめも作っています。ハッシュタグ #東京スクラッパー から抽出しています。

第3回Webスクレイピング勉強会@東京のまとめ - Togetterまとめ


感想



 どれも見応えのある内容で、非常に楽しかったです。私の発表については、AWSの便利さが伝わったようです。ただ、懇親会等で話を聞いていると、まだまだクラウド破産を恐れる人が沢山いるということが解りました。これは、長年使っていると、全く気にならなくなります。ただ、外から見るとそのような恐れがあるというは、改めて認識できるようになりました。同じカテゴリーの人と話していては気がつかないことですね。ありがたいです。
 また、Pythonのクローラー本に対するリクエストが多かったです。しばらく動けそうにないので、誰かやりたい人がいたらご連絡くださいw

PR
anemoneの解説を含めて、Rubyによるクローラー開発の本を書きました。
クローラーの概念から実際の構築・運用手順を網羅しています。


See Also:
RubyでWebスクレイピングの話をしてきました。第1回Webスクレイピング勉強会@東京
「第2回Webスクレイピング勉強会@東京」に参加&発表してきました
『Rubyによるクローラー開発技法』を書きました
Rubyによるクローラー開発技法の目次
個人ブログの存在感は、自分が思っているより大きいのかもしれない。或いは書籍の流通の話
『Rubyによるクローラー開発技法』の増刷決定しました
本を書く前に準備したこと、執筆中にしていたこと


参照:
第3回Webスクレイピング勉強会@東京(最終回)

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

プログラミング・レスで5分でサックリWebスクレイピング「kimonolabs」

 「Rubyによるクローラー開発技法」で付録か何かで書こうか悩んだ末に書かなかったのが、kimonolabsの話です。kimonolabsは、クローラー/スクレイピングをオンラインで実行できるWebサービス(SaaS)です。クローラー本を書いておいて何ですが、9割の人は自分でクローラーを作らずに、この手のサービスを利用すれば事足りると思います。(書かなかった理由は、Ruby縛りサービスの継続性とスケジュールの問題です。主に最後)
f:id:dkfj:20140813182433p:plain

kimonolabsとは?



 kimonolabsは、先述のとおりWebスクレイピングをしてくれるSaaSです。会員登録してChromeの拡張をいれれば、すぐに使えるようになります。一般的に、Webスクレイピングする場合は、次のような手順が必要です。

  1. 対象ページのダウンロード
  2. ダウンロードしたページから、特定の箇所を抜き出す
  3. 抜き出したデータの保存

 対象ページのダウンロードとデータの保存はそれほど手間ではないのですが、2番目の特定の箇所を抜き出すというのが中々やっかいです。一般的には、各プログラムの構文解析器(パーサー)を使って、xpathやCSSセレクタなので要素を指定して抜き出します。パーサーは、Ruby製のNokogiriやPython製のbeautifulsoupなど色々あります。このツールの使い方を覚えるというのと、xpathやCSSセレクタなどHTMLの要素の指定の仕方の2つを覚える必要があります。それ以前に、プログラミングの仕方を知っていないと出来ません。
 kimonolabsを使えば、このあたりを一切知らなくてもスクレイピングできるという優れ物です。対象のページをブラウザで開いた上で、kimonolabの拡張を開いて取得したい部分をマウスで選択するだけです。簡単便利です。
 私も幾つかスクレイピングのSaaSを知っていたのですが、ここまで完成度が高いものはしりませんでした。前回の、Webスクレイピング勉強会@東京で教えて貰いました。

kimonolabsの使い方 APIの登録編



 それではAmazonの商品ページを対象に、実際の使い方を試してみましょう。アカウントとかは適当に作って下さい。今回の取得対象は、このページを対象に、全体のランキングとカテゴリーごとのランキングを取得するようにします。
※ステマ

1.対象ページを開いた上で、kimonolabのchromeの拡張を起動
f:id:dkfj:20140813185212p:plain

2.取得対象を指定する
 マウスのカーソルを取りたいあたりに移動させると、黄色で選択してくれます。その上で、不要な部分があれば☓をしていけば良いです。複数指定したい場合は、kimonoのメニューの+ボタンで増やします。
f:id:dkfj:20140813185746p:plain
ちなみに、中々思い通り選択してくれなくて、イライラすることもたまにあります。

3.取得データを確認する
 メニューの右の方の、=みたいなのや<>でプレビューできます。
f:id:dkfj:20140813190340p:plain
f:id:dkfj:20140813190356p:plain

4.APIとして登録
 確認して問題なければ、Doneボタンを押してAPIとして登録します。その際に、適当に名前を付けます。
f:id:dkfj:20140813190506p:plain

5.APIの詳細設定
 通知や訪問間隔などの設定をします。
f:id:dkfj:20140813190550p:plain

kimonolabsの使い方 データの取得編



 取得したデータは、kimonoの管理画面もしくはアプリケーションIDとAPIキーを指定したURLで取得できます。
・データのバージョン指定
http://www.kimonolabs.com/api/{VERSION_NO}/{API_ID}?apikey={YOUR_API_KEY}
・時系列で取得
http://www.kimonolabs.com/api/{API_ID}?apikey={YOUR_API_KEY}&kimseries=1

 1時間ごとに取得したデータを、時系列でみた例です
f:id:dkfj:20140813182447p:plain

 データは、json,csv,rssで取得できます。データをプログラムから呼び出して、さらにゴニョゴニョするといったことも簡単にできます。

まとめ



 かなり完成度も高く、無料の範囲でも充分利用できます。今回は紹介していませんが、ページネーションなどもできます。クローラーやWebスクレイピングは、それ自体が目的には成り得ません。収集したデータをどう活用するか、そこからが本当のスタートです。だから、データ取得の部分をサービスで代用できるのであれば、できるだけ利用して楽をすべきです。ということで、今は必要でなくても、頭の片隅に記憶しておいたら良いと思いますよ。
 ついでに言うと、自作のクローラーはどういった場合に作ればよいのか?夏休みの自由研究と、既成のサービスで機能的に物足りなくなってからで良いと思います。もしくは、このサービスをキッカケにクローラーに興味をもったら、この本あたりで中でどのように動いているのか勉強するのも良いですよ。※ステマ


See Also:
『Rubyによるクローラー開発技法』を書きました
KimonoLabsと今後のサービスのあり方のはなし


参照:
オープンデータのためのスクレイピング
第1回Webスクレイピング勉強会@東京 (全3回) - connpass


Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例