Mojo::UserAgentがスクレイピングツールとして便利


# 概要

スクレイピングでやりたいことは、
だいたい以下のようなもの。

■データを取得する
-> GETでHTMファイルL/XML(RSS)ファイル/画像ファイルなどを取得
-> POSTでフォームにSubmitする(ログイン/パスワードDL)
-> 並列で取得する

■取得したデータから必要なデータを抽出する
-> HTMLをParseして、CSSセレクタでデータを抽出する
-> JSONをParseして、データを抽出する
-> XML(RSS)をParseして、データを抽出する

■取得したデータを保存する
-> ファイルとして保存する

Mojo::UserAgentは、上記の処理が簡単に書けるようになっている。

# インストール

MojoliciousをインストールするとMojo::UserAgentも入る。
HTTPSにアクセスすることもあるので、IO::Socket::SSLも入れる。

```shell
$ cpanm Mojolicious IO::Socket::SSL
```

# データを取得する

基本的な使い方は以下のようになる。

```perl:sample1.pl
use strict;
use warnings;
use utf8;
use Mojo::UserAgent;

# インスタンス生成
my $ua = Mojo::UserAgent->new;

# 最大リダイレクト回数を設定
$ua->max_redirects(5);

# GET設定
my $tx = $ua->build_tx(GET => 'http://www.perl.com/');

# UserAgentを設定
$tx->req->headers->user_agent('user-agent');

# REFERERを設定
$tx->req->headers->referrer('http://www.perl.com/');

# Cookie設定
$tx->req->cookies({name => 'foo', value => 'bar'});

# トランザクションスタート
$tx = $ua->start($tx);

# 取得
# $res は、 Mojo::Message::Response
if (my $res = $tx->success) {
    print $res->body;
}
else {
    my ($err, $code) = $tx->error;
    print $code ? "$code response: $err\n" : "Connection error: $err\n";
}
```

POSTを行う場合は、以下のようになる。

```perl:sample2.pl
use strict;
use warnings;
use utf8;
use Mojo::UserAgent;

# フォームの値
my %params = (
    login_id => 'hogehoge',
    login_password => 'fugafuga',
);

# インスタンス生成
my $ua = Mojo::UserAgent->new;

# 最大リダイレクト回数を設定
$ua->max_redirects(5);

# POST設定
my $tx = $ua->build_tx(POST => 'http://hogehogehugahuga.com/login', form => \%params);

# REFERERを設定
$tx->req->headers->referrer('http://hogehogehugahuga.com/');

# トランザクションスタート
$tx = $ua->start($tx);

# 取得
# $res は、Mojo::Message::Response
if (my $res = $tx->success) {
    print $res->body;
}
else {
    my ($err, $code) = $tx->error;
    print $code ? "$code response: $err\n" : "Connection error: $err\n";
}
```

# 取得したデータを扱う

jsonからデータを取得する場合は、
以下のように記述することができる。

```perl:jsonsample.pl
# Pointerを指定してデータを取得する
my $title = $res->json('/results/0/title');

# hashに変換してから取得する
my $hash = $res->json
my $title = $hash->{results}[0]{title};
```

HTMLからデータを取得する場合は、
以下のように記述することができる。

```perl:htmlsample.pl
# CSSセレクタで取得
# (セレクタを渡したdomメソッドの返り値はMojo::Collectionになっている)
# https://metacpan.org/pod/Mojo::Collection
my $texts = $res->dom('h2 > a')->text;

# メソッドチェーンで取得
my $texts = $res->dom->html->head->title->text;
```

ファイルを保存する場合は、
以下のように記述することができる。

```perl:filesample.pl
use strict;
use warnings;
use utf8;
use Mojo::UserAgent;

# インスタンス生成
my $ua = Mojo::UserAgent->new->max_redirects(5);

# GETを設定
my $tx = $ua->get('latest.mojolicio.us');

# 取得
# $res は、Mojo::Message::Response
if (my $res = $tx->success) {
    $res
    ->content # Mojo::Content::Single
    ->asset   # Mojo::Asset::File
    ->move_to('/home/uchiko/mojo.tar.gz'); # ファイルを保存
}
else {
    my ($err, $code) = $tx->error;
    print $code ? "$code response: $err\n" : "Connection error: $err\n";
}
```

# その他

## non-blocking

POSTをnon-blockingで行える。
https://metacpan.org/pod/Mojo::UserAgent#post

## Websocket

websocketも扱える。
https://metacpan.org/pod/Mojo::UserAgent#websocket

## デバッグモード

MOJO_USERAGENT_DEBUG環境変数で、UserAgentの詳細な動作を確認することができる。
https://metacpan.org/pod/Mojo::UserAgent#DEBUGGING


## Inactivity timeout

大きなデータを送っていると「Inactivity timeout」というエラーが出るかもしれない。
その時は以下のようにタイムアウト値を大きめに設定する。
http://mojolicio.us/perldoc/Mojolicious/Guides/FAQ#What_does_Inactivity_timeout_mean

```shell
$ MOJO_INACTIVITY_TIMEOUT=10000 perl scrape.pl
```


# 参考

Mojo::UserAgent
https://metacpan.org/pod/Mojo::UserAgent