Rを使ったウェブスクレイピングで過去未来のZeppのライブスケジュールを取得する

最近見たサイトの分析が素晴らしく、

僕もウェブスクレイピングしたい!

と思ってを買ってきて読み通したところ、とりあえずたどたどしいながらもデータ取得できるようになったのでさっそく使ってみました。
その結果として表記のデータを得ることができたので、欲しい人には共有しますのでどうぞ

Zepp大阪ベイサイドのライブスケジュール(csv)(2018-01-19取得)

また下に示すスクリプトを使えば大阪ベイサイド以外のZeppのスケジュールも取得できます。

【前口上】

商売にとって近所でイベントがあるかどうかというのはめちゃくちゃ大きな影響を持ちます。
しかし雑誌や新聞に記載されているものはもちろんですが、インターネット上で公開されているイベント情報も分析者にとっては分析できる状態にはなっていません。

ある商人がZepp大阪ベイサイドの近隣で商売をしていました。
Zepp大阪ベイサイドでは日々アツいライブが繰り広げられていますが、ライブは毎日開催されているわけではありません。
この商売にとってZepp大阪ベイサイドでライブがあるかどうかというのは致命的に大きな影響を持つため、過去の売上を分析するためにも、未来の売上を予測するためにもZepp大阪ベイサイドのライブスケジュールを把握しないことには話になりません。

Zepp大阪ベイサイドのスケジュール

商人は仕入れの計画を立てるためにこのスケジュールの特に将来部分を毎日チェックしていました。
しかし、このサイト、

  • 年月ごとにページが違う
  • どの程度の将来まで予定が入っているのかわからない
  • ボタンを押してみないと開演時間がわからない

などといった理由により、スケジュールを正確に把握するためにはページをいくつもチェックしなければなりません。
しかしその割には頻繁に更新されるわけでもないので段々と面倒になってきて抜け漏れが発生するようになります。
かといってアルバイトを毎日雇う価値のある仕事でもない。
よって彼の唯一の部下であるコンピュータ君にやらせることにしました。世の中で彼だけが正確に、無料で、文句も言わず毎日働いてくれます。

【RによるZeppスケジュール取得スクリプト】

データを取得するにあたって最大の課題は取得したいデータが表にはなっていないこと、そして

  • 日によって複数回ショーがある場合がある
  • ショーによって複数種類のチケットがある場合がある

点です。
それを踏まえれば、下記の三種類のデータがスケジュールデータを構成していることがわかります。

  • カレンダー:ある日にどのアーティストがライブをするか
  • ショー:その日にアーティストは何時からショーを開始するか
  • チケット:そのショーに設定されているチケットはいくらなのか

これらのデータをうまく整理しながらデータを集めなければなりません。

今回は作成するスケジュールは『ショー』を単位にすることにしました。
ショースケジュールからカレンダーへは簡単に変換できますし、チケットを単位にするのはおかしい気がしたからです。

library(tidyverse)
library(lubridate)
library(stringr)
library(rvest)

# 取得対象年の範囲を設定
start_year <- 2017
end_month <- 2018

# 取得urlの指定
baseurl <- "http://hall.zepp.co.jp/osakabayside/schedule.html"

# ほかのZeppも取得したい場合はこちらをどうぞ
# baseurl <- "http://hall.zepp.co.jp/namba/schedule.html"
# baseurl <- "http://hall.zepp.co.jp/sapporo/schedule.html"
# baseurl <- "http://hall.zepp.co.jp/tokyo/schedule.html"
# baseurl <- "http://hall.zepp.co.jp/divercity/schedule.html"
# baseurl <- "http://hall.zepp.co.jp/nagoya/schedule.html"


# Zeppの月別のスケジュールページから日ごとの情報を取得する関数
# 日ごとの情報にはアーティスト名、ツアー名が含まれる
getShowDetail <- function(html){
  tibble(datebox = html_nodes(html, xpath = "//*[@id = 'sc']/div[@class = 'right']/div[contains(@class, 'date_box')]")) %>%
    mutate(
                  date_header = datebox %>%
                          html_nodes(xpath = "div[@class = 'date_header clearfix']") %>%
                          map(as.list),
                  detail = datebox %>%
                          map(. %>% html_nodes(xpath = "div[@class = 'date_cont']/div[@class = 'detail']"))
                  ) %>%
    mutate(
         day = map_int(date_header, . %>%
                       html_nodes(xpath = "p[@class = 'date_day']/span") %>%
                       html_text() %>%
                       iconv(from = "UTF-8") %>%
                       parse_integer()
               ),
         artist = map_chr(date_header, . %>%
                          html_nodes(xpath = "div[@class ='info whBox']/div/p[@class = 'artist']") %>%
                          html_text() %>%
                          iconv(from = "UTF-8") %>%
                          paste0("")
                  ),
         tour = map_chr(date_header, . %>%
                        html_nodes(xpath = "div[@class ='info whBox']/div/h2") %>%
                        html_text() %>%
                        iconv(from = "UTF-8") %>%
                        paste0("")
                )
         ) %>%
    select(day, artist, tour, detail) %>%
    return()
}

# 上で取得した日ごとの情報(detail)から具体的なショースケジュールを取得する関数
# ショースケジュールは開場時間と開演時間とその日何度目の公演かという情報で成り立つ
getShowSchedule <- function(detail){
        tibble(
               show = map(detail, . %>%
                          html_nodes(xpath = "dl")
                  ),
               kaijo = map_chr(show, . %>%
                               html_nodes(xpath = "dd[1]") %>%
                               html_text() %>%
                               str_extract("^[^/]*") %>%
                               str_extract("\\d\\d:\\d\\d")
                       ),
               kaien = map_chr(show, . %>%
                               html_nodes(xpath = "dd[1]") %>%
                               html_text() %>%
                               str_extract("開演:\\d\\d:\\d\\d") %>%
                               str_extract("\\d\\d:\\d\\d")
                       )
               ) %>%
        mutate(n = row_number()) %>%
        return()
}
# 取得した日ごとの情報(detail)からチケット情報を取得する関数
# ショー単位にチケット情報がある。また1階席、2階席などチケットには複数種類ある。
getTicketInfo <- function(show){
        tibble(
               pp = show %>%
                       html_nodes(xpath = "dd") %>%
                       .[-1] %>%
                       html_text()
                ) %>%
        transmute(
               price = pp %>%
                       str_replace_all("[[:space:]|,|\\\\]", "") %>%
                       str_extract("^\\d*") %>%
                       paste0(""),
               place = pp %>%
                       str_extract("[^[:space:]]*$") %>%
                       paste0("") ,
               ticket_num = row_number()
               ) %>%
        return()
}


yearlist <- seq(start_year, end_year)
monthlist <- 1:12
ymlist <- as.Date(as.character(NULL))
for(i in yearlist){
  for(j in monthlist){
    ymlist <- c(ymlist, make_date(year = i, month = j, 1))
  }
}

# 以降で上で定義した関数を使って目的のデータを作る。
zepp.all <- tibble(ymlist = ymlist) %>%
  mutate(year = year(ymlist), month = month(ymlist)) %>%
  mutate(monthstr = paste0("0", month) %>% str_sub(start = -2L, end = -1L)) %>%
  mutate(url = paste0(baseurl, "?year=", year, "&month=", monthstr)) %>%
  mutate(html = map(url, read_html, encoding = "UTF-8"))

zepp.detail <- zepp.all %>%
        mutate(showdetail = map(html, getShowDetail)) %>%
        unnest(showdetail) %>%
        mutate(date = make_date(year, month, day)) %>%
        select(date, artist, tour, detail)
zepp.show <- zepp.detail %>%
        mutate(show = map(detail, getShowSchedule)) %>%
        select(-detail) %>%
        unnest(show)
zepp.ticket <- zepp.show %>%
        mutate(ticket = map(show, getTicketInfo)) %>%
        select(-show) %>%
        unnest()

# for output
zepp.price <- zepp.ticket %>%
        mutate(pricenum = paste0("ticket_price", ticket_num)) %>%
        select(date, n, price, pricenum) %>%
        spread(pricenum, price)

zepp.place <- zepp.ticket %>%
        mutate(placenum = paste0("ticket_place", ticket_num)) %>%
        select(date, n, place, placenum) %>%
        spread(placenum, place)

zepp.out <- zepp.show %>%
        select(date, artist, tour, kaijo, kaien, n) %>%
        left_join(zepp.price, by = c("date", "n")) %>%
        left_join(zepp.place, by = c("date", "n")) %>%
        select(-n)

この構成だとチケット情報が最大4までしか取得できないのですが、とりあえずはどのようなライブが、いつ、何時から始まるのかは分かるようになりました。
チケット数を柔軟に取得できるほうがいいのでしょうが、頭が疲れてきてわからんくなったので投げました。。良案募集。

(2018-04-26追記)
チケット情報が何種類あったとしてもデータ取得できるようにコードを修正。
あとwindowsでも正しく取得できるように文字コード周りも微調整しました。

あと正規表現などテキスト処理がヘタクソなのもバレてしまって恥ずかしい。
勉強中なので許してください。

【取得データ】

Zepp大阪ベイサイドのライブスケジュール(csv)(2018-01-19取得)

取得したZepp Osaka baysideスケジュールテーブル

上でも説明しましたが今回作成したスケジュールは『ショー』を単位にすることにしました。
ショースケジュールからカレンダーへは簡単に変換できますし、チケットを単位にするのはおかしい気がしたからです。
カレンダーデータにしたい場合は下記のようにしてください。

zepp.calendar <- zepp.out %>%
  select(date, artist, tour) %>%
  unique()

参考書籍

コメントを残す