ishiducaの日記 このページをアンテナに追加 RSSフィード

2011-05-18

[]WWW::Pixiv::Download的なもの 17:12 WWW::Pixiv::Download的なもの - ishiducaの日記 を含むブックマーク はてなブックマーク - WWW::Pixiv::Download的なもの - ishiducaの日記 WWW::Pixiv::Download的なもの - ishiducaの日記 のブックマークコメント

【補足】最新のバージョンは ishiduca/p5-www-pixiv-download - GitHub にあります。こちらは、ユーザーエージェントを WWW::Mechanize から LWP::UserAgent に変更しています(2011.05.22)

WWW::Pixiv::Download があればいいんだけど見当たらないので、それっぽいの書いてみた。

残っているTODOも多いんだけど、全部こなすと時間かかりそうなので、めどが付いたところで晒してみますね。

出来ること

  • イラストコンテンツの Mサイズのダウンロード
  • イラストコンテンツの オリジナルサイズのダウンロード
  • 漫画コンテンツの Mサイズのダウンロード

SYNOPSIS

use WWW::Pixiv::Download;

my $client= WWW::Pixiv::Download->new(
    pixiv_id => 'your pixiv id',
    pass     => 'your pixiv password',
);

my $illust_id = 'xxxx123';

$client->download($illust_id);
  • 上記の例だと、illust_id "xxxx123" のオリジナルサイズのイラストをダウンロードできます。
  • ただし、このコンテンツが漫画の場合は、漫画を構成する(複数の)イラストをダウンロードすることになります。
  • コンテンツのMサイズのイラストをダウンロードする場合はオプションをつけます
$client->download($illust_id, {
    mode => 'medium',
});
  • ファイル名を変更して保存する場合は
$client->download($illust_id, {
    file_path => 'foo/bar/file.name',
});

実際にコマンドラインから illust_id を指定してコンテンツをDLするスクリプト(pixiv_dl.pl)

#!/usr/bin/env perl
use strict;
use warnings;
use WWW::Pixiv::Download;
use Config::Pit;

@ARGV or die qq(usage: $0 illst_id);

my $illust_id = $ARGV[0];
my $config = pit_get('www.pixiv.net', require => {
    pixiv_id => 'pixiv_id',
    pass     => 'pass',
});
die 'pit_get failed.' if ! %$config;

my $client = WWW::Pixiv::Download->new(
    pixiv_id => $config->{pixiv_id},
    pass     => $config->{pass},
);

$client
    ->download($illust_id, { mode => 'medium' })
    ->download($illust_id)
    ;

exit 0;
$ perl pixiv_dl.pl 12345

みたいな感じで

WWW/Pixiv/Download.pm

package WWW::Pixiv::Download;

use strict;
use Carp;
use URI;
use WWW::Mechanize;
use Web::Scraper;
use File::Basename;

my $home   = 'http://www.pixiv.net';
my $login  = "${home}/";
my $mypage = "${home}/mypage.php";

sub new {
    my $class = shift;
    my %args  = @_;

    $args{pixiv_id} or Carp::croak qq(not found "pixiv_id");
    $args{pass}     or Carp::croak qq(not found "pass");

    $args{user_agent} = WWW::Mechanize->new( autocheck => 1)
        unless exists $args{user_agent};
    bless \%args, $class;
}

sub download {
    my $self      = shift;
    my $illust_id = shift;
    my $args      = shift || {};
    my($mode, $file_path) = ($args->{mode}, $args->{file_path});
    $self->{illust_id} = $illust_id;

    die qq(not found param "illust_id".) unless $illust_id;

    if ($self->login &&  $self->check_mode_eq_medium) {
        my $scraper = scraper {
            process '//div[@class="works_display"]/a[1]', 'to_bigImg_href' => '@href';
            process '//div[@class="works_display"]/a[1]/img[1]', 'img_src' => '@src';
        };

        my $res = $scraper->scrape($self->{user_agent}->content);

        if ($mode eq 'medium' or $mode eq 'm') {
            $self->_save_content($res->{img_src}, $file_path);
        } elsif ($res->{to_bigImg_href} =~ m|mode=big|) {
            $self->{user_agent}->get(join '/', $home, $res->{to_bigImg_href});
            $scraper = scraper {
                process '//img[1]', 'img_src' => '@src';
            };
            $res = $scraper->scrape($self->{user_agent}->content);
            $self->_save_content($res->{img_src}, $file_path);
        } else {
            $self->{user_agent}->get(join '/', $home, $res->{to_bigImg_href});
            my $content = $self->{user_agent}->content;
            while ($content =~ m|unshift\('(http://([^\']+)?)'|g) {
                $self->_save_content($1, $file_path);
            }
        }
    }

    $self;
}

sub _save_content {
    my $self = shift;
    my $url  = shift;
    my $file_path = shift;

    $self->{user_agent}->get($url);
    $file_path ||= basename $self->{user_agent}->uri->as_string;
    $self->{user_agent}->save_content(re_file_name( $file_path));
}

sub check_mode_eq_medium {
    my $self  = shift;
    my $uri   = $self->{user_agent}->uri;
    my %query = $uri->query_form;
    unless ($query{mode} eq 'medium' and $query{illust_id} eq $self->{illust_id}) {
        $self->to_mode_eq_medium;
        $self->check_mode_eq_medium;
    }
    return 1;
}

sub to_mode_eq_medium {
    my $self = shift;
    my $uri  = URI->new(join '/', $home, 'member_illust.php');
    $uri->query_form(
        mode      => 'medium',
        illust_id => $self->{illust_id},
    );
    $self->{user_agent}->get( $uri );
}

sub login {
    my $self = shift;
    my %args = @_;

    if ($self->{logged_in} ne '1') {
        $self->{user_agent}->get( $login );
        $self->{user_agent}->submit_form(
            form_name => 'loginForm',
            fields    => {
                mode     => 'login',
                pixiv_id => $self->{pixiv_id},
                pass     => $self->{pass},
            },
        );

        die qq(login failed $!) unless $self->{user_agent}->res->is_success;
        my $uri = $self->{user_agent}->uri->as_string;
        die qq(login failed. "$uri" now.) unless $uri eq $mypage;

        $self->{logged_in} = '1';
    }
    return 1;
}

sub re_file_name {
    my $file_path = shift;
    if (-e $file_path) {
        my ($file_name, $path, $suffix) = fileparse($file_path);
        my ($name, @exts) = split /\./, $file_name;
        my $timestmp = time;
        $file_path = re_file_name(join '', $path, (join '.', $name, $timestmp, @exts));
    }
    $file_path;
}

1;

todo

  • 漫画コンテンツのオリジナル画像のDLに対応させる
  • エラー処理が不十分
  • UserAgentをLWP::UserAgentに返る
  • メタデータ(イラストのタイトル、作者名、etc,)を扱えるようにする
  • 進捗状況を扱えるようにする
  • コールバック関数を使えるように
  • 漫画コンテンツの画像へのURLを正規表現以外の方法で取得する

SerenaSerena2014/05/07 03:36That's the thikinng of a creative mind

トラックバック - http://perl.g.hatena.ne.jp/ishiduca/20110518