Python
R
ggplot2
BeautifulSoup
web-scraping,data-extraction

Amazonギフト券の安値情報をPythonでWebスクレーピング&Rで解析

More than 3 years have passed since last update.

アマオクでいかに安くAmazonギフト券を手に入れるか

皆さまはアマオクというサイトをご存知でしょうか?Amazonギフト券を売買できるサイトで、大体5~10%程度の割引率で取引されています。

このサイトで、できるだけお得にギフト券を買うにはどうしたらよいのか?例えば火曜日は割引率が良い、25日近辺は割引率が悪いなど何らかの傾向はあるのか?

幸いなことにアマオクは過去の取引データを一般公開しています。この取引データをPython + Beautiful Soupでスクレーピングし、Rで解析したのが本記事の内容です。

先に結論を書きますと、以下になります。
- 額面と値引き率は関係がない
- 有効期間と値引き率は関係がない
- 現在は割高。92.5~95%になるのを待って買う。
- どの曜日でも値引き率は変わらない
- どの日でも値引き率は変わらない
- 日中の方が他の時間帯より若干安い

環境

  • Windows 8.1 64bit
  • Python 3.5
  • Beautiful Soup
  • R version 3.2.1

Webスクレーピング

使用したPythonのコードは以下です。流れとしては、

  1. スクレーピングするページの範囲を取得
  2. Beautiful Soupを使い取引情報を一行ずつ取得
  3. 情報をcsvファイルに書き込み
  4. 次のページに移行
  5. 2~5を、1で取得した範囲で繰り返し

です。

amaoku_scraping.py
#! coding: UTF-8

from bs4 import BeautifulSoup
import urllib.request
import time

file = open("C:/Users/user/amaoku_transaction_data.csv", 'w') 

# get last page index
last_index = 0
html = urllib.request.urlopen("https://amaoku.jp/past_gift/index_amazon/")
soup = BeautifulSoup(html, "lxml")
a_s =  soup.find(class_="pager_link").find_all('a')
for a in a_s:
    if a.string.replace(u"\xa0", u" ") == u'最後 »':
        last_index = int(a.get('href').split('/')[-1])

# get auction data from a page 
last_index = 20
page_index = 0
while page_index <= last_index:
    url = 'https://amaoku.jp/past_gift/index_amazon/' + str(page_index)
    html = urllib.request.urlopen(url)
    soup = BeautifulSoup(html, 'lxml') 
    rows = soup.find('table', class_='contacttable').find('tbody').find_all('tr')
    # get sales data from a page
    for row in rows:
        line_elements = []
        # if the row is a header, skip 
        if row.has_attr('class') and ['class'][0] == 'tr_tit':
            continue
        items = row.find_all('td')
        for item in items:
            # if the item is empty, skip
            if item.string == None:
                continue
            # clean the string
            element = item.string.replace(',', '').replace('&nbsp;', '').replace('\xa0', '').replace(u'円', '').replace('%', '')
            line_elements.append(element)
        line = ','.join(line_elements)
        if line == '':
            continue
        file.write(line + '\n')

    print("Page {0} processed".format(page_index))
    time.sleep(1)
    # 20 items per a page
    page_index += 20

file.close()
print("Task completed")

Rで解析

前処理

ファイルをread.csvで読み込み、各列に名前を入れます。日時データはDateクラスに変換しておきます。

uri <- "D:/workspace/amaoku_analyze/amaoku_transaction_data.csv"
dat <- read.csv(uri, header=T, fileEncoding="UTF-8", stringsAsFactors = F)
names(dat) <- c("biddate", "facevalue", "bidprice", "discount", "validdate")
dat$biddate2 <- as.Date(dat$biddate)
dat$validdate2 <- as.Date(dat$validdate)

一応書くと、
- biddate : 購入日時
- facevalue : 額面
- bidprice : 購入価格
- discount : 値引き率
- valid date : 有効期限
です。

NaNなどがある行を調べると、170個ありました。

sum(!complete.cases(dat))  # 170

消しておきます。

dat = dat[complete.cases(dat),]

データは176899行7列あります。

> str(dat)

'data.frame':   176899 obs. of  7 variables:
 $ biddate   : chr  "2015/12/20 18:58" "2015/12/20 18:03" "2015/12/20 18:03" "2015/12/20 18:01" ...
 $ facevalue : int  10000 5000 5000 20000 3000 5000 5000 3000 10000 3000 ...
 $ bidprice  : int  9750 4825 4825 19300 2880 4800 4825 2895 9700 2895 ...
 $ discount  : num  97.5 96.5 96.5 96.5 96 96 96.5 96.5 97 96.5 ...
 $ validdate : chr  "2015/12/20" "2016/12/20" "2016/11/20" "2016/12/20" ...
 $ biddate2  : Date, format: "2015-12-20" "2015-12-20" "2015-12-20" ...
 $ validdate2: Date, format: "2015-12-20" "2016-12-20" "2016-11-20" ...

額面が大きいほど値引き率は高くなるか?

額面の数字が大きいほど値引き率は高くなりそうです。実際はどうなのでしょうか?

require(ggplot2)
ggplot(dat, aes(facevalue, discount)) + geom_point() + labs(x="Face value [yen]", y="Discount rate [%]")

facevalue_vs_discountrate.png

パッと見ではそういう傾向は無さそうに見えます。
回帰直線の傾きを見てみましょう。

>summary(lm(discount ~ facevalue, data=dat))

Coefficients:
              Estimate Std. Error  t value Pr(>|t|)    
(Intercept)  9.401e+01  5.586e-03 16828.37   <2e-16 ***
facevalue   -1.812e-05  2.516e-07   -72.03   <2e-16 ***
---
Signif. codes:  0 *** 0.001 ** 0.01 * 0.05 . 0.1   1

傾きは-1.812e-05。Pr(>|t|)の値を見ると有意です。つまり、額面が1000円増えると値段は0.02%下がるという傾向があります。ほぼ誤差の範囲ですね。

結論 : 額面と値引き率は関係がない

有効期間と値引き率は関係あるか?

普通に考えると、有効期間が短いものほど需要が低いので、値引き率が高くなりそうです。本当のところはどうなのでしょう?

有効期限と購入日時から有効期間を出し、値引き率と一緒にプロットします。

dat$timediff <- as.numeric(difftime(dat$validdate2, dat$biddate2, units = "days")) / 365.24
ggplot(dat, aes(timediff, discount)) + geom_point() +
    labs(x="Valid period [year]", y="Discount [%]")

validperiod_vs_discountrate.png

こちらも特に傾向はなさそうです。回帰直線の傾きはさっきと同じ方法で、-0.099743(p < 2e-16)でした。

有効期間1年のもので値引き率が低くなっているように見えますが、単に標本数が大きいので分布の裾野が広いだけでしょう。以下、ヒストグラムです。

結論 : 有効期間と値引き率は関係がない

ggplot(dat, aes(timediff)) + geom_histogram(binwidth = 1/12) + xlim(-1, 5) +
    labs(x="Valid period [year]", y="Frequency")

validperiod_histogram.png

通年での値引き率の変化はどうか?

一年を通じて見たとき、値引き率はどう変わるのか?安い季節はあるのでしょうか?

ggplot(dat, aes(biddate2, discount)) + geom_point(size=1) +
    ylim(75, 100) + labs(x="Date", y="Discount [%]")

date_vs_discountrate.png

横軸の数字は2015年の月です。蛇行するような動きを見せています。今回取得したデータは過去1年間の為、季節変動については詳しくは分かりませんが、通年のデータを見る限り今の季節は割高に見えます。グラフを見る限りですと、92.5~95%が相場に見えます。

結論 : 現在は割高。92.5~95%になるのを待って買う。

曜日と値引き率は関係があるのか?

曜日についても調べます。土日はサイトの利用者数が多いので売り手側に有利になり、値引き率が悪くなると考えられます。

weekdays_names=c("月曜日","火曜日","水曜日","木曜日","金曜日","土曜日","日曜日")
dat$weekdays <- factor(weekdays(dat$biddate2), levels= weekdays_names)
wddf <- aggregate(discount ~ weekdays, dat, mean)
gggplot(data=dat, aes(weekdays, discount)) + geom_boxplot() + 
    ylim(75,110) + labs(x="Day of the week", y="Discount rate [%]")

dayoftheweek_vs_discountrate.png

結論 : どの曜日でも値引き率は変わらない

日にちと値引き率は関係があるのか?

25日近辺は給与日なので利用者の財布が潤い、多少条件が悪くても売れるので値引き率が悪くなる可能性があります。

dat$days <- factor(format(as.POSIXct(dat$biddate), format="%d"))
ggplot(dat, aes(days, discount)) + geom_boxplot() + 
    ylim(75,100) + labs(x="Day of a month", y="Discount rate [%]")

dayofamonth_vs_discountrate.png

結論 : どの日でも値引き率は変わらない

一日の中の時間帯で値引き率は変わるのか?

深夜や早朝、昼間は利用者数が少なくなり値引き率が改善するのではないのか?調べます。

dat$hours <- factor(format(as.POSIXct(dat$biddate), format="%H"))
ggplot(dat, aes(hours, discount)) + geom_boxplot() +
    ylim(75,100) + labs(x="Hour of a day", y="Discount rate [%]")

hourofaday_vs_discountrate.png

23~翌朝8時までは価格が気持ち高めに出ています。あまり大した差ではありませんが、安値を狙うなら日中がよいでしょう。

結論 : 日中の方が他の時間帯より若干安い

最後に

いかがでしたでしょうか?この情報を元に、ユーザーの皆様が少しでもAmazonギフト券が安く買えれば幸いです。