楽天アフィリエイトの確定レポートを自動取得してタブ区切りテキストに出すスクリプト

なんのためのスクリプトかという話はここでは書かない。とりあえず過去記事にリンクしておく。

windows用のActive Perl5.8.8で動作確認済み。 もちろんLinuxなんかでも動くだろう。

なお、SSL(https)なページを経由しなければならないので、 Crypt::SSLeay(Net::SSLeay)などのモジュールも必要。 これはActivePerlの標準のPPMでは入ってない/うまく取得できないはず。 ここなどにある解説を参考にしながらいれる。

#!/usr/local/bin/perl -w
#
# 使い方: rakuaffl_get.pl ユーザーID 開始日付(YYYY-MM-DD) 取得したい週数 結果を格納するファイル名
# 例1 perl rakuaffl_get.pl hogehoge@example.com 2007-01-31 4 hogehoge.txt
# 結果は日付どおりに並んでないのはご愛嬌。
#
# タブ区切りテキストなのでそのままエクセルに
# コピペ可能なのであとは日付ソートやオートフィルタ等
# 好きに加工、分析する。
# リクエスト間隔を3秒とっているので、件数が多かったり
# 取得したい週数が多かったりするとそれなりに時間がかかります。
#
# Shift_JISな環境を想定しているので、それ以外の場合は
# jcode呼んで->sjisとかやっているところを->eucとかにするとよいかも。

use strict;
use Term::ReadKey;
use Date::Simple;
use WWW::Mechanize;
use HTML::TableContentParser;
use HTML::Strip;
use Jcode;

my $userid = $ARGV[0];
my $date = Date::Simple->new($ARGV[1]);
my $week = $ARGV[2];
if ($week > 30) {
    print "一度に取得できる最大週数は30週です\n";
    exit(0);
}
my $file = $ARGV[3];

# パスワード
ReadMode('noecho');
print "input password: ";
my $password = ReadLine(0);
chomp $password;

# ログイン
my $mech = WWW::Mechanize->new();
my $start = "http://affiliate.rakuten.co.jp/";
$mech->agent_alias('Windows IE 6');
$mech->get($start);
$mech->form_name("LoginForm");
$mech->field(u => $userid);
$mech->field(p => $password);
$mech->submit();
if (jcode($mech->content())->sjis =~ /入力に誤りがあります/) {
    print "IDかパスワードが違うみたい\n";
    exit(0);
}else {
    print "\n";
}

# 全データを取得してファイルに格納
open(FILE, ">$file") || die "ファイルが作成できません\n";
foreach (my $i=0; $i<$week; $i++) {
    foreach my $data (&getdata($date->year, $date->month, $date->day)) {
        print FILE join("\t", @{$data}), "\n";
    }
    $date -= 7;
}
close(FILE);

# 指定した日付を基点に過去1週間のデータをすべて取得する。
sub getdata {
    my ($end_year, $end_mon, $end_mday) = @_;
    my $url  = "https://partner.afl.rakuten.co.jp/af/a_report.cgi?";
       $url .= "Command=disp&SubCommand=result&page=1&limit=100&span=1week";
       $url .= "&end_year=$end_year&end_mon=$end_mon&end_mday=$end_mday";
       $url .= "&status=1";
    my @result = ();
    my $flag = 1;
    while ($flag) {
        sleep(3);
        $mech->get($url);
        my $content = $mech->content();
        my $parser = HTML::TableContentParser->new();
        my $strip = HTML::Strip->new();
        if (jcode($content)->sjis !~ /指定された条件に該当する成果はありませんでした/) {
            my @tables = $parser->parse($content);
            shift(@{$tables[0][22]{"rows"}}); # 最初の行は見出し
            foreach my $row (@{$tables[0][22]{"rows"}}) {
                my @data = ();
                for my $cell (@{$row->{cells}}) {
                    my $d = $strip->parse($cell->{"data"}); # 余計なfontタグ等除去
                    $d =~ s/\s//g; # 余計な空白除去
                    push(@data, $d);
                }
                push(@result, [@data])
            }
        }
        if ($mech->find_link(text_regex => qr/次の100件/i)) {
            $url = $mech->find_link(text_regex => qr/次の100件/i)->url();
        } else {
            $flag = 0;
        }
    }
    return @result;
}

__END__

それPlaggerでできるよというツッコミが多数予測されるが(笑)あえて使わなかった理由はWWW::Mechanizeその他のCPANモジュールの使い方を忘れかけてたのを頭に入れなおしたかったから。本当はPHPかJavaでやりたかったのだが、この種の話はやはりperlとCPANに分がある。実質80行程度でできた。

楽天の店舗の中の人へ楽天Webサービス利用者から愛をこめて

要するに、楽天のAPIで取得できる情報には「いまいち」なものも少なくないのでなんとかしないとAPI利用者≒アフィリエイターとして商品を紹介してくれる人々から嫌われてしまうかもしれない、というお話。 ちなみに筆者は商売柄いろいろ試しているというだけで実際のアフィリエイターではないので視点がズレてるのかもしれないが。

注文情報等を抽出するための店舗向けAPIはだいぶ前からあったのだが、店舗向けだったのでそう大きな話題になってはいない。この1月に公開された【楽天ウェブサービス】RAKUTEN WEBSERVICEは完全に一般向け。特定のURLにGETリクエストを送るだけで商品情報をXML形式で取得できる。

サービス方式自体はそう珍しくないが、やはりジャンルや商品の数は圧倒的である。今後、アフィリエイターの皆さんがこぞって使うことになるだろう。

ところで、いろいろ試してみてわかったのだが、商品名(itemname)や商品説明(itemcaption)の部分がまずいことになっているデータが結構あった。原因は店舗側の商品情報の編集の仕方にある。

たとえば、文字飾りや区切り文字のつもりでテキストを駆使しているのがアダになっているケース。

XMLデータ

楽天上での実際の商品画面

XMLデータ

楽天上での実際の商品画面

実際の商品画面上では問題なく読めても、データだけを抽出するAPI経由ではこんなことになってしまう。これらのデータを使って作ったアフィリエイト用のコンテンツの見栄えはかなりお粗末なものになるだろう。

問題はやはり店舗の中の人が商品情報を楽天に投入する段階にある。 商品説明の部分ではHTMLタグを使ってもよいのだが、楽天API経由で出力されるときはタグはすべて抜かれてしまう。 どうしても区切り線を設けたいならhrタグかなにかでも使い、細かい調整をCSSでやるべきなのだが、そこを「-----」といったテキストで間に合わせようとしたのがアダになっている。

本来は「テキスト」は「データ」を表現するためのものであって、「デザイン」を表現したいならHTMLタグを使おう、という、一見何の変哲もない原則がここでは重要な意味を持つ。 また、商品名の部分に商品名以外の情報をいれるべきではないことも当然である。

この問題はまた、楽天の商品情報登録機能がデータとデザインに明確な分かれ目を設けていない という潜在的な問題も浮き彫りにする。商品情報にHTMLタグを使えることは店舗の個性を出すうえで重要な自由度だが、データだけをやりとりするためのAPIという概念とは相反してしまう。

とにかく、情報とデザインとをごっちゃにしてはいけないのだ。 楽天大学ではこういう概念を店舗に教え、伝えきれないのだろうか。 素人さんはPC用の画面の見た目をどうにかするだけで精一杯だろうしなあ。

ほかにも、楽天の商品画面上では「(この商品に関する)詳細はこちら」とかいってリンクをはっているのが、API経由で指摘するとaタグが抜かれてしまうため、「詳細はこちら」という文字列だけになってしまって商品説明としてはまったく意味を成さなくなっているケースもかなりあった。

で、アフィリエイターの皆さんはこれに対処するためにどうするか?

  • APIで得られるItemCaption項目の活用はあきらめて、画像と商品名と価格だけを活用する。

    これはこれでアリだと思う。実際、アマゾンなんかのProductDescription項目を表示しているアフィリエイトサイトは少ないし。 ただし、少ない情報で商品の羅列だけ、というのは、アフィリエイトサイト自体の質の低下を招くのではないだろうか。自動販売機じゃないんだから。
  • そのうちAPI取得できるようになるのであろう「商品レビュー」の内容をItemCaption代わりにすべく、それをじっと待つ。

    これもやはりアマゾンのAPIを使っているサイトによく見られる傾向(のような気がする)
  • APIで取得できるデータにhtmlのタグをも含めた商品説明を使えるようになるのを待つ。

    ItemCaptionの内容をCDATA扱いにしてしまうことで、なんというか、楽天の店舗サイト上に映っているHTMLをそのまんまAPIでも取得できる、みたいな。たぶんそんなサービスはされないとは思うが。
  • 店(shopcode)で判断する。

    これをされると店舗側はかなり怖い。 つまり、API取得でのItemCaptionがちゃんとした情報になっている店ではほとんどちゃんとしてるし、 そうでない店では多くの商品においてそうでない。したがって、itemcaptionがイケてないと判断した店をshopcodeで判断してはじく(他にも店あるしー)→当分あるいは半永久的にアフィリエイターに紹介してもらえない→じわりじわりと店の売り上げが・・・
といった行動が考えられる。

ちなみに、HTTP/HTMLにおいてデータとデザインの区別をうまいこと明確化しながら表現しようという話にはたとえばmicroformatsといった概念が提唱されており、既に一部で使用されているらしい。が、本格的に普及するかというと。。。?

でまあ、こういうことって楽天の中の人に限らず他のECサイト構築ソフトウェアあるいはサービス一般に言えることである。RSSがどうだのWebサービスAPIだのなんだのと、「データだけを見せてくれ」という要求にも簡潔に答えるためには、Webサイトの設計段階でそもそもデータとデザインとの区別というか線引きというかそのへんうまくやらないと、あとあと困ることになる。

PHP使ってるんならstrip_tags()やhtmlspecialchars()くらい覚えておこう。

セキュリティに100%は無く、追っかけだすとキリがないのでどっかで区切るしかない。 が、それにしてもIPA(独立行政法人情報処理推進機構)の講演会受付フォームのXSS脆弱性は、 初歩的な対策をとっていなさすぎる。

IPA XSS
ぼくはまちちゃん!(Hatena) - IPAたんからの返事
攻撃方法はというと(上記サイトのソースから抜粋)
<body onload="document.getElementById('f').submit()">
<h1>IPA XSS</h1>
<form action="https://www.ipa.go.jp/app/form/lecture_receptions/add" method="post" id="f">
<input type="text" name="data[LectureReception][lecture_id]" value="9">
<input type="text" name="data[LectureReception][theme_id][18]" value='"><script>alert("こんにちはこんにちは!!")</script>'>
こんな初歩的な。。。

どんなソフト使ってんのかなと思ってIPAのサイトのヘッダとかを眺めていたら

Server: Apache
X-Powered-By: PHP/4.4.4
ということらしい。PHP使ってるなら、フォームからPOSTされる全ての値に対してstrip_tags()するだけでも、 あるいは次の画面で表示する前にhtmlspecialchars()するだけでも、 上のような初歩的な話は防げるのだが。

もちろん一般論としてWebサイトの攻撃方法はいろいろあってそれを防ぐ方法もひとつじゃないわけだが、それにしても「POSTされた値は全て疑って余計な文字を抜くor変換する」みたいな基本的な手法くらいは覚えておいたほうがいいだろう。

と、いう記事の草稿を書いてしばらくおいといた間に IPAセミナー受付フォームにおけるクロスサイト・スクリプティングのぜい弱性について (情報処理推進機構:重要なお知らせ 2007/2) ということになってました。

追記:
ブックマークのコメントでなんだかウダウダ書いてる方がいるようだが、 Webサイトの攻撃方法もいろいろあって防ぐ方法もひとつじゃないということは上に書いたとおり。 いちいち反論する気はないのであしからず。 とりあえず 安全なウェブサイトの作り方でも読んでということで。(って、ああ、これもIPAのコンテンツか(笑))。それからstrip_tagsだけみたいに思われるのもなんだかなのでタイトルも変更。