work.log

元エンジニアの備忘録的ブログ

PerlのCoroを使ってスクレイピング処理を並行化

投稿:2014-03-29 20:49  更新:

Perl モジュールの Coro のメモ書きです。

処理内容を見なおしたりキャッシュとか使ってみたりしながら、スクレイピング処理を効率的にできるように色々試しているのですが、その中で Coro というモジュールの存在を知ったので試してみました。

スクレイピング処理で一番時間がかかる部分はコンテンツを取ってくる所だと思うので、ここをマルチプロセス化と思いましたが、fork するよりはスレッドを使った方が省力化なのでこちらを採用。

まずは、RSS から個別記事の URL を抜き出してコンテンツを取得という処理を普通に書いてみます。

#!/usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;
use XML::FeedPP;

    my $url = 'http://example.jp/feed';
    my $res = &lwp_get($url);
    my $feed = XML::FeedPP->new($res->content);
    my @url = ();

    foreach my $entry ($feed->get_item()) {
        $res = &lwp_get($entry->link());
        print $res->title, "\n";
    }

sub lwp_get {

    my $url = shift || return(0);
    my $lwp = '';
    my $res = 0;

    $lwp = LWP::UserAgent->new(
        timeout => 30
    );

    $res = $lwp->get($url);

    if (!$res->is_success) {
        $res = 0;
    }

    return($res);

}
exit;

これを自分のブログを使って実行するとこんな結果に。

# time perl lwp_get.pl 
PerlのLWPでCacheを使う | work.log
PerlのCache::Fileが便利なのでメモ | work.log
PerlからWordPressを操作する | work.log
WordPressのwp_optionsテーブルって結構便利 | work.log
PerlとかPHPで外部コマンドを実行した時の戻り値を判定する | work.log
WordPressのstyle.cssに任意のバージョンを設定する | work.log
WordPressで利用するファイルのExpiresヘッダを見直す | work.log
WordPressで作ったコンテンツを最適化する試み | work.log
WP Super Cache の手動インストールメモ | work.log
WordPressでMinified化したstyle.min.cssを使う | work.log
-----
0.343u 0.087s 0:05.99 7.0%      10+5348k 0+0io 0pf+0w

実行時間は 5.99sec でした。

次は、同じ処理を Coro を使って並行化してみます。

#!/usr/bin/perl

use strict;
use warnings;
use Coro;
use Coro::LWP;
use LWP::UserAgent;
use XML::FeedPP;

    my $url = 'http://example.jp/feed';
    my $res = &lwp_get($url);
    my $feed = XML::FeedPP->new($res->content);
    my @result = ();

    my $lock = Coro::Semaphore->new(5);

    foreach my $entry ($feed->get_item()) {

        push @result, async {

            my $guard = $lock->guard;
            $res = &lwp_get($entry->link());
            print $res->title, "\n";

        };

    }

    $_->join for @result;

sub lwp_get {

    my $url = shift || return(0);
    my $lwp = '';
    my $res = 0;

    $lwp = LWP::UserAgent->new(
        timeout => 30
    );

    $res = $lwp->get($url);

    if (!$res->is_success) {
        $res = 0;
    }

    return($res);

}
exit;

まだ良く理解していない部分が多いですが、async で囲んだ部分がマルチスレッド化されて処理されるようになり、Web サーバの負荷を考慮して Coro::Semaphore で同時に処理するスレッド数を 5 に制限しています。

有り難いことに、Coro::LWP をロードするだけで LWP の待ち時間をなるべく無くするように勝手に処理してくれるみたいです。

先ほどと同様に自分のブログで試してみます。

# time perl lwp_coro.pl
PerlとかPHPで外部コマンドを実行した時の戻り値を判定する | work.log
PerlのLWPでCacheを使う | work.log
PerlのCache::Fileが便利なのでメモ | work.log
WordPressのwp_optionsテーブルって結構便利 | work.log
PerlからWordPressを操作する | work.log
WordPressで利用するファイルのExpiresヘッダを見直す | work.log
WP Super Cache の手動インストールメモ | work.log
WordPressのstyle.cssに任意のバージョンを設定する | work.log
WordPressでMinified化したstyle.min.cssを使う | work.log
WordPressで作ったコンテンツを最適化する試み | work.log
-----
0.403u 0.177s 0:03.59 15.8%     10+7126k 0+0io 0pf+0w

実行時間は 3.59sec と先ほどに比べて早く処理が完了しました。

表示されたタイトルの順番が先ほどと違うのを見ると、各リクエストが並行で行われたのが良くわかると思います。

とりあえず、何とか理解している範囲はここまで。

暫くは勉強も兼ねて Coro を使って色々試してみようと思います。

おすすめのVPSサーバ

  • OSが選べる
  • VPS同士でLANが組める
  • 複数台構成向き

このブログで使っています。

  • 転送量が多いサービスに
  • 借りてるのは3年間一度もdown無し!

よく見られている記事

  • 本日
  • 週間
  • 月間