Amazonマーケットプレースの出品情報を抽出する(GreaseMonkey使用)




Amazonには、マーケットプレース(通称マケプレ)という中古の商品を購入出来るシステムがあります。マーケットプレースを使用すると欲しい商品を安価で手に入れることが出来るので、非常にお得です。


ですが、マーケットプレースの出品者一覧ページが非常に見づらく不便です。
出品者情報は、価格が安い順に1ページ当たり15件の情報が表示されるのですが、例えばある商品の出品一覧ページを見てみると、こんなに長くなっています。


個人的には、古本なんてそこそこの品質のものが安く手に入ればよいだけなので、この情報量は親切を通り越して却って分かり辛くなっています。

というわけで、今回はマケプレの出品者一覧を加工して見やすくしてみます。

方針を決める


必要なデータだけ抽出したいならスクレイピングすればよいのですがAmazonの規約上、スクレイピングすることはダメだと明記してあるので、この方法は取れません。

というわけで、今回はhtmlデータはブラウザからそのまま取得します。
その上で、ブラウザ側のユーザ定義スクリプトで不要なデータを削除したり見た目を変えることで情報を整理します。

ブラウザに表示されているデータを加工するのに便利なツールといえばGreaseMonkeyです。
GreaseMonkeyはFirefoxのプラグインですが、特定のサイトを訪れた時に指定したjavascriptを走らせる事が出来ます。

今回はGreaseMonkeyプラグインを使って、javascriptからサイトのデザインを調整する事にします。

元ネタとして使用するページを決める

元ネタとしてPCサイトを使用すると、余計な情報が多すぎて加工の手間が増えます。
という訳で今回はケータイ用のサイトを元に加工する事にしました。

amazonのケータイサイトは、URLさえ指定すればパソコンのブラウザからも閲覧できます。
デフォルトでは、以下の様な表示になります。

ケータイサイトだとPC用サイトよりずいぶんシンプルになりますが、これでもまだ長すぎます。
これを、1ページの内容が1画面に収まるところまで持ってい行きます。


ケータイ版マケプレのURL構造

表示デザインを加工する前に、アマゾンのURL体系を確認しておく必要があります。
これに関しては以前調べたので、興味がある場合は、下記のページを参照してください。
携帯サイトで,マケプレの出品情報をチェックする


GreaseMonkeyのスクリプトのダウンロードと解説

という訳で作ったスクリプトは以下の通りです。
(プログラムに興味が無い人は下のほうまで読み飛ばしてください)

下記のリンクをクリックすると、ソースが閲覧できます。
amazon.co.jp_mobile_MarketPlace_.user.js

ちょっと長めのスクリプトですが、大きく分けると2つの関数で構成されています。

(function( d, onLoadProc ) {
	...
})(document, function($) {
	...
});



最初の関数はjQueryのライブラリを読み込んだ上で、2つ目の関数を実行しています。
jQueryをロードしているのは、今回のスクリプトはDOM操作が多く、素のjavascriptだとちょっと面倒だったのが理由です。

2つ目の関数が、実際の加工処理になります。
ケータイサイトは、タグがシンプルで余り構造化されておらず、idやclassの指定もほとんど有りません。


ですので、まずは各出品者情報の切れ目を作るところからはじめます。

    //------------------------------------
    // "カートに入れるボタン"を線に変える
    //  (ここがアイテムの変わり目)
    //------------------------------------
    console.log( "replace center tag" );
    $( "center" ).after( '<hr class="item-end" />' );
    $( 'hr.item-end' ).css( 'height', '1px' )
                      .css( 'border', 'none' )
                      .css( 'margin', '1px 0px' )
                      .css( 'border-top', '1px #fcc solid' );
 
    // centerタグを外す
    $('center').each( function() {
        $(this).replaceWith( $(this).html() );
    });
    $('input.cartButton').parent().css( 'display', 'inline' );


ページを見ると、丁度”カートに入れる”ボタンが切れ目になりそうだったので、これを目印にします。
このボタンはcenterタグで囲まれているので、その後ろにhrタグを入れます。ほかのhrタグと混ざらないようにclass指定もしておきます。ついでに、ボタンのcenterタグを外した上で改行を抑制します。


各明細の切れ目が印がついたら、各明細データを集めます

    //-------------------------------------
    // 出品情報を含むノードの一覧を取得
    //-------------------------------------
    console.log( "collect body children tag" );
    $("body").contents().each( function() {
        itemContents.push( $(this) );
 
        // 商品の変わり目に到達したかチェック
        if ( $(this).is( 'hr.item-end') ) {
            console.log( '  contents len:' + itemContents.length );
            console.log( itemContents );
 
            items.push( itemContents );
            itemContents = new Array();
        }
    });


区切りの目印を入れたので、一旦この単位でデータを集めて配列に入れておきます。
通常だったら、ここでタグのidやclassを指定して対象データを特定するところなのですが、id/classの指定が無いので全部取得してしまいます。また、テキストノードも欲しいのでcontents()メソッドを使用します。


次は、集めたタグに対して意味が特定できるようにします。

    //-------------------------------------
    // 商品情報をマークアップする
    //-------------------------------------
    console.log( "markup item info len=" + items.length );
    for ( loop = 0; loop < items.length; loop++ ) {
        console.log( "  item[" + loop + "]" );
 
        var curContents = items[ loop ];
 
        // 先頭は書籍タイトルなので無視
        if ( loop == 0 ) {
            continue;
        }
 
        // (たぶん)1明細目 -> 先頭に有るページ表示を切り詰める
        while( curContents.length > 21 ) {
            //console.log( "omit contents↓" );
            //console.log( curContents[0] );
            curContents.shift();
        }
 
        console.log( "  itemContents len=" + curContents.length );
 
        // 改行
        curContents[ 1 ].addClass( 'br1' );
 
		// ...中略...
 
        // コンディション+屋号
        curContents[ 6 ].wrap( '<span class="cond_marchant"></span>' );
 
        // 改行
        curContents[ 7 ].addClass( 'br3' );
 
        // コンディション説明
        curContents[ 8 ].wrap( '<span class="conditionDesc"></span>' );
 
		// ...中略...
    }


出品の明細データは同じデータ構造の繰り返しになります。
先ほど配列に入れたデータの中身を見ながら、addClass()メソッドでマーキングします。
テキストノードにはclass指定が行えないため、spanタグで括っておきます。


次はいよいよ見た目の変更です。

    //--------------------------------------------------
    // マークアップされたコンテンツのスタイルを改変する
    //--------------------------------------------------
    console.log( "change style" );
    $( '.br1' ).hide();
    $( '.br2' ).hide();
    $( '.br3' ).hide();
    $( '.br4' ).hide();
    $( '.price'             ).css( 'color', '#990000' ).css( 'font-size', '14pt' );
    $( 'span.cond_marchant' ).css( 'color', '#999999' );
    $( 'span.rating'        ).css( 'color', '#999999' );
    $( '.conditionDesc'     ).hide();
//  $( '.conditionDesc'     ).css( 'color', '#999' ).css( 'font-size', '9pt' );
    $( '.shipInfoLink'      ).hide();
    $( '.detailInfoLink'    ).hide();


付与したclass名指定でcssの適用を行います。また不要なデータはここでhide()指定します。
今回は該当項目がありませんが、テキストノードを直接隠したい場合はhide()ではなくdelete()をコールする必要があります。


コンディション表記と屋号は同じタグ内にスペース区切りで表示されているだけなので、両者の区別を行います。

    //-------------------------------------------------------
    // コンディションと屋号が同じタグに混ざっているのを分離
    //-------------------------------------------------------
    console.log( "split condition information" );
    $( 'span.cond_marchant' ).each( function(){
        var marchantText = $( this ).text();
        marchantText = String( $.trim( marchantText ) );
 
        // コンディションと屋号を切り分ける
        var splitPos = marchantText.indexOf( '\xa0' );
        var cond  = marchantText.substring( 0, splitPos );
        var title = marchantText.substring( splitPos+1 );
 
        if ( title.length > 15 ) {
            title = title.substring( 0, 15 ) + "...";
        }
 
        // 別々のタグに分ける
        $( this ).replaceWith( '<p class="cond">' + cond + '</p>' + 
                               '<p class="marchant">' + title + '</p>' );
    });
 
    // 分離した各タグにスタイルを適用
    console.log( "change style2" );
    $( 'p.cond'     ).css( 'margin',   '0px' )
                     .css( 'display',  'inline-block' )
                     .css( 'width',    '90px' );
 
    $( 'p.marchant' ).css( 'margin',   '0px' )
                     .css( 'display',  'inline-block' )
                     .css( 'width',    '200px' )
                     .css( 'font-size', '9pt' )
                     .css( 'color', '#999999' );


該当データのテキストを取得した上で改めてタグ付けした後、replaceWith()で新しい値をセットしなおしています。2つの項目はスペースではなくnbspで区切られていたため、区切り文字の検出に、indexOf( ‘\xa0’ );を指定します。


最後に、細々とした調整をします。

    //---------------------------
    // ヘッダ部を見やすくする
    //---------------------------
    console.log( "change header style" );
    $( 'a[name="Used"]' ).after( "<br />" );
    $( 'font:contains("-----------")' ).remove();






ここまで加工した後のレイアウトは以下の通りです。


サイズは横が500pxで、縦が400pxぐらいになりました。
ここまで小さくなれば、楽勝で1画面に収まってくれます。


ここまで省略するのは極端だという場合は、css適用を変えることで表示項目を調整できます。
例えば下の例では、コンディションコメントを表示させたままにしていますが、これでも元のレイアウトよりはずいぶん一覧性が良くなっています。

関連記事

コメントを残す

メールアドレスが公開されることはありません。