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

2010-04-10

[][Web::Scraper][Webスクレイピング]pixivのお気に入りリストを抽出するスクリプトを直した 22:41 [Web::Scraper][Webスクレイピング]pixivのお気に入りリストを抽出するスクリプトを直した - ishiducaの日記 を含むブックマーク はてなブックマーク - [Web::Scraper][Webスクレイピング]pixivのお気に入りリストを抽出するスクリプトを直した - ishiducaの日記 [Web::Scraper][Webスクレイピング]pixivのお気に入りリストを抽出するスクリプトを直した - ishiducaの日記 のブックマークコメント

WWW::Mechanizeを使ってpixivへログインして、ごにょごにょする」がうまく作動してなかったようなので直した。変わったといっても

  • 文字コードが euc_jp から utf-8 になってた
  • ログイン時の引数に mode が追加されていた

くらいだけど。

pixiv2pl

#!/usr/bin/perl
use strict;
use warnings;
use Encode;
use WWW::Mechanize;
use Web::Scraper;

my $enc_utf8 = find_encoding('utf-8');

my $home = 'http://www.pixiv.net/';
my $mech = new WWW::Mechanize( autocheck => 1 );
$mech->get($home);
# my_userid,my_passwordは自分のを使ってね
$mech->submit_form(
    form_number => 2,
    fields => {
        'mode'     => 'login',
        'pixiv_id' => 'my_userid',
        'pass'     => 'my_password',
    },
);

my $scraper = scraper {
    process "#illust_c5", 'lists' => scraper {
        process "li", 'li[]' => scraper {
            process "a",   'link'    => '@href';
            process "img", 'image'   => '@src';
            process "div", 'comment' => 'TEXT';
        };
    };
};

(my $temp =<<"TEMP") =~ tr/\n//d;
<div>
<a href="${home}%%LINK%%">
<img src="%%SRC%%" border="0" /></a>
<div>%%COM%%</div></div>
TEMP
;

if ($ENV{'GATEWAY_INTERFACE'}) {
    require CGI;
    print CGI::header('text/html; charset=utf-8');
}
print <<'HTML';
<!doctype html>
<html>
<head>
<meta charset='utf-8' />
<title>pixiv::myfavorite</title>
<head>
<body>
HTML
;

for (my $i = 1; $i <= 20; $i++) {
    $mech->get("${home}bookmark_new_illust.php?mode=new&p=$i");
    my $res = $scraper->scrape($mech->content);
    for my $li (@{$res->{lists}->{li}}) {
        local $_ = $temp;
        s/%%LINK%%/$li->{link}/;
        s/%%SRC%%/$li->{image}/;
        s/%%COM%%/$li->{comment}/;
        print $enc_utf8->encode("$_\n");
    }
}
print qq(<body>\n</html>\n);

exit 0;

なんかCGI.pm使うのかったるかったので、html部分は手書きにしたけど、最近は別のモジュール使う方がいいのかしらん?

ついでなかんじがあるけど、Web::Scraper 初めて使ってみました。Xpathとか分からんので苦労したな〜。あ、あとリファレンスを多少でも理解してないと難しいのが分かったぞ

参照

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

2009-05-31

[][WWW::Mechanize][Webスクレイピング]WWW::Mechanizeを使ってpixivへログインして、ごにょごにょする 21:39 [WWW::Mechanize][Webスクレイピング]WWW::Mechanizeを使ってpixivへログインして、ごにょごにょする - ishiducaの日記 を含むブックマーク はてなブックマーク - [WWW::Mechanize][Webスクレイピング]WWW::Mechanizeを使ってpixivへログインして、ごにょごにょする - ishiducaの日記 [WWW::Mechanize][Webスクレイピング]WWW::Mechanizeを使ってpixivへログインして、ごにょごにょする - ishiducaの日記 のブックマークコメント

WWW::Mechanize を使ってpixiv(SNS)にログイン-> 「お気に入りユーザー新着イラスト」のページ(1~10頁)のデータを抜き出してリストにする

#!/usr/bin/perl
use strict;
use warnings;
use encoding 'euc-jp', STDOUT => 'utf-8';
use WWW::Mechanize;

my $mech = new WWW::Mechanize(autocheck => 1);

my $uri  = 'http://www.pixiv.net/';
$mech->get($uri);

$mech->submit_form(
    fields => {
        pixiv_id => 'userID',
        pass     => 'password',
    },
);
$mech->get('http://www.pixiv.net/mypage.php');

(my $reg =<<'REG') =~ tr/\n//d;
<a href="(member_illust\.php\?mode=medium&illust_id=[0-9]{7})">
<img src="([^"]+)" border="0" /></a><br />
<div class="pdgTop5">([^<]+)</div>
REG
;

if( $ENV{GATEWAY_INTERFACE} ){
    require CGI;
    print CGI::header('text/plain; charset=utf-8');
}
foreach my $c (1..10){
    $mech->get("http://www.pixiv.net/bookmark_new_illust.php?mode=new&p=$c");
    my $html = $mech->content;
    while( $html =~ /$reg/gis ){
        my $page = "${uri}$1";
        print join("\t", $3, $2, $page), "\n";
    }
}

exit 0;

本当なら Web::Scraper を使えばいいんだろうけど、勉強中なので。

参照

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

2009-05-11

[][][][]Re: package宣言をして名前空間を定義する 20:24 Re: package宣言をして名前空間を定義する  - ishiducaの日記 を含むブックマーク はてなブックマーク - Re: package宣言をして名前空間を定義する  - ishiducaの日記 Re: package宣言をして名前空間を定義する  - ishiducaの日記 のブックマークコメント

端折りすぎました。

package宣言をして名前空間を定義する(はじめてのモジュール その2)では、以下のコードの中で「our」を使っていたんですが、モジュールファイル内で作ったグローバル変数をスクリプト内で使うためのインポート/エクスポートの処理が書いてありませんでした。このままでは、eto.pl ではグローバル変数のエラーが返ってしまいます。kits さんがはてブで「ourによる変数名のパッケージ名省略が可能なのは、あくまで宣言したブロック(ファイル)の範囲内のみ」とご指摘くださいました。

ex: ishiduca.pm(モジュールファイル)このままだと、$Year は eto.pl では使えない

package ishiduca; # モジュールファイル名でpackage宣言する
use strict;
our( $Year ); # 明示的には$ishiduca::Year
$Year = 1919;
# 以下省略
1;

ex: eto.pl(モジュールを使用するスクリプト)

#!/usr/bin/perl -wl
use strict;
use ishiduca;

print $Year; # ishiduca.pm でour(グローバル変数)宣言されているので、
             # エラーは出ない
             # 明示的にはprint $ishiduca::Year;

端折った部分を加えるとこう

ishiduca.pm

package ishiduca;
use strict;
our( $Year );
$Year = 1919;

# 以下、端折った部分
our(@ISA, @EXPORT);

require Exporter;
@ISA = qw( Exporter );

@EXPORT = qw( $Year );

1;

もしくは、eto.pl で、明示的に $ishiduca::Year とする

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

2009-05-06

[][][]package宣言をして名前空間を定義する(はじめてのモジュール その2) 14:26 package宣言をして名前空間を定義する(はじめてのモジュール その2) - ishiducaの日記 を含むブックマーク はてなブックマーク - package宣言をして名前空間を定義する(はじめてのモジュール その2) - ishiducaの日記 package宣言をして名前空間を定義する(はじめてのモジュール その2) - ishiducaの日記 のブックマークコメント

注:この記事には、間違っているところがあります。それは僕の理解不足によるところですが、ありがたいことに kits さんが、はてブでご指摘くださいました。ここでは、どこが間違っているのかは、明らかにしません。この記事の最後に間違えを訂正した記事へのリンクを張っておきますので、この記事を読み終えた後に続けて読んでいただけると幸いです。また、この記事へのツッコミも歓迎します。(2009.05.10 追記)


僕が購読している『ミニマルPerl』では詳しい説明が省略されているので、web上の文献を横断的に読んで箇条書きにしてみようと思ったけど、なかなか覚えきれないので、モジュール作成に関連したところだけを箇条書きにしてみる。

  • モジュール(ファイル)を作る時に、最初に宣言する。
  • package Module_name は Module_name の名前空間を定義する。
  • モジュール定義を保持するファイルでは、Module_nameはファイル名に対応する名前とパスを使用する。例えばMoudule_name を ishiduca と定義した場合は、ishiduca.pmだし、ishiduca::nengo とした場合は、ishiducaというディレクトリ下の nengo.pm。
  • 宣言した後に使われる(名前空間内で定義された)変数、サブルーチンは明示的にはModule_name::変数名、Module_name::サブルーチン名となる。
  • 宣言しない場合は暗黙的に package main される。
  • package名前空間の有効範囲は、package宣言をした位置から別のpackage文が現れるまで(ブロック内で宣言した場合は、そのブロック内)
  • package名前空間内でのみ使用する変数はmy宣言。他の名前空間でも使用する場合はour宣言する。

ex: ishiduca.pm(モジュールファイル)

package ishiduca; # モジュールファイル名でpackage宣言する
use strict;
our( $Year ); # 明示的には$ishiduca::Year
$Year = 1919;
# 以下省略
1;

ex: eto.pl(モジュールを使用するスクリプト)

#!/usr/bin/perl -wl
use strict;
use ishiduca;

print $Year; # ishiduca.pm でour(グローバル変数)宣言されているので、
             # エラーは出ない
             # 明示的にはprint $ishiduca::Year;

出力

1919

色々端折ってるからこんなに単純じゃないけど、イメージとして。


この記事には続き(Re: package宣言をして名前空間を定義する)があります。続けて読んでいただけると幸いです。



参照にした

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

2009-05-04

[]テンプレートを使ってモジュールを作成する(はじめてのモジュール) 19:33 テンプレートを使ってモジュールを作成する(はじめてのモジュール) - ishiducaの日記 を含むブックマーク はてなブックマーク - テンプレートを使ってモジュールを作成する(はじめてのモジュール) - ishiducaの日記 テンプレートを使ってモジュールを作成する(はじめてのモジュール) - ishiducaの日記 のブックマークコメント

『ミニマルPerl』で紹介しているモジュールのテンプレート(一般的な書式くらいの意)から新しいモジュールを作成して、ターミナルからモジュールを使えるようにする(スクリプトから使えるようにもする)。今日はその手順(端折って書く)。

  1. モジュールを置くディレクトリを作る
  2. テンプレートを編集してモジュールを作成、保存する
  3. モジュールをテストする(構文エラーチェック)
  4. モジュールの置き場所を指定する
  5. モジュールをテストする(スクリプトで使用する)
  6. モジュールの置き場所を設定ファイルに記述する

1. モジュールを置くディレクトリを作る

(適当に)ホームディレクトリに「myperl」ディレクトリをつくってみた

$ mkdir myperl

2. テンプレートを編集してモジュールを作成、保存する

テンプレートは以下の物を参照(それぞれ詳しい内容は別記事で書く...予定)

package Module_name;
use strict;
use Carp;

our(@ISA, @EXPORT, @EXPORT_OK);
our( module_vars, module_subs );

require Exporter; @ISA = qw/ Exporter /;

@EXPORT    = qw/ module_vars module_subs /;
@EXPORT_OK = qw/ module_vars module_subs /;

sub sub_ex{
    # some code;
}

1;

これが例えば干支を獲得する関数(get_eoto)を定義するモジュールだった場合

package ishiduca;
use strict;
use Carp;

our(@ISA, @EXPORT, @EXPORT_OK);

require Exporter;
@ISA = qw/ Exporter /;

@EXPORT    = qw/ get_eto /;
@EXPORT_OK = qw//;

# 西暦(YYYY)を引数にして、干支を返す
sub get_eto{
    my $usage = ":Usage:get_eto \"YYYY\"";
    @_ == 0 and
      # warn __PACKAGE__, $usage
      carp   __PACKAGE__, $usage
        and exit 255;
    local $_ = shift;
    my @eto = qw/申 酉 戌 亥 子 丑 寅 卯 辰 巳 午 未/;
    return $eto[ $_ % 12 ];
}

1;

(適当に)ishiduca.pmでmyperlディレクトリで保存

3. モジュールのテスト

まず、文法のテスト(オプションのcは構文エラーのチェック)

$ perl -wc ishiduca.pm

問題はなければ

ishiduca.pm syntax OK

と出る

4. モジュールの置き場所を指定する

この状態で使おうとしても次のような警告がでる

Can't locate ishiduca.pm ... compilation aborted

ので、Perlの検索リストを保管する環境変数PERL5LIBにモジュールが置かれたディレクトリのパスを追加する。それにはターミナルで

$ export PERL5LIB=$PERL5LIB:$HOME/myperl

と打つ。これで現在のシェルセッションで有効になる

5.モジュールをテストする(スクリプトで使用する)

テスト用のスクリプト(test.pl)

#!/usr/bin/perl -wl
use strict;
use ishiduca qw/ get_eto /;

# 引数を YYYY 形式のものだけに選別する
@ARGV = grep{ /^\d{4}$/ }@ARGV;
# 引数がない場合は、エラー処理
@ARGV == 0 and die "Usage: $0 YYYY [YYYY...]\n";

$, = "\n";
print map{ get_eto $_ }@ARGV;

exit 0;

実際に試してみる

$ perl test.pl 2007 2009 1971 1919

実行結果

亥
丑
亥
未

ワンライナーでもテスト

$ perl -Mishiduca -wle 'print get_eto $ARGV[0];' 2010
寅

6. モジュールの置き場所を設定ファイルに記述する

期待した結果が得られました。ただこの状態だと現在のシェルセッションでしかモジュールの位置をシェルに伝えられません。シェルにログインする度に export PERL5LIB=$PERL5LIB:$HOME/myperl と打つ代わりに、シェルのスタートアップファイル(~/.bash_login Kornシェルの場合は~/.profile)に、export PERL5LIB=$PERL5LIB:$HOME/myperl を追加します。このファイルが見当たらない場合は、新たに作成します。

これで、新たなセッションでログインしてもモジュールが使えるようになりました。



今回はモジュールの作成から使用可能にする設定までを重点にまとめ(?)てみました。

僕が購読している『ミニマルPerl』はモジュールの取扱はあまり重点的ではないのでそろそろ別の文献も必要かな

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