取得したいデータがあるウェブページのURLを見てどのような規則で表示されているかを推察する。
https://www.data.jma.go.jp/obd/stats/etrn/view/hourly_s1.php?prec_no=44&block_no=47662&year=2022&month=8&day=10&view=
この例では,prec_no,block_noは恐らく気象観測所に関する番号であろう,year,month,dayが年月日だろうと当たりが付く。このようにパラメータを含む形でURLが記載されている場合でページソースにHTMLテーブルやXMLの形式でデータがある場合(ウェブ画面を右クリックしソースを表示すると分かる)は,パラメータをプログラムで変えることで異なるウェブページに機械的にアクセスしデータを取得できる。 ただし,Yahoo!ファイナンスのようにウェブサーバーの負荷の問題からウェブスクレイピングを禁止してるところもあるので利用規約を読むことが必要。ウェブスクレイピング自体は合法的である。
# 出力ファイル
DB <- 'weather.duckdb' # データベース名
F.O <- 'weather.csv' # CSVファイル名
# 気象観測所
site <- data.frame(
id = 47662, # 番号
name = 'Tokyo') # 名称(データベースのテーブル名として使う)
# 対象日時(テーブル取得のためのURLに適用する日時)
lt <- as.POSIXlt('2022-08-10') # POSIX準拠ローカル時間
year <- 1900 + lt$year
month <- 1 + lt$mon
day <- lt$mday
url <- paste0('https://www.data.jma.go.jp/obd/stats/etrn/view/hourly_s1.php?prec_no=44&block_no=', site$id, '&year=', year, '&month=', month, '&day=', day, '&view=')
cat('URL:', url, fill = T) # 作成したURLを表示
## URL:
## https://www.data.jma.go.jp/obd/stats/etrn/view/hourly_s1.php?prec_no=44&block_no=47662&year=2022&month=8&day=10&view=
library(rvest)
read_html(url) |> html_table() -> tbl
tbl
## [[1]]
## # A tibble: 1 × 2
## X1 X2
## <lgl> <lgl>
## 1 NA NA
##
## [[2]]
## # A tibble: 1 × 2
## X1 X2
## <lgl> <lgl>
## 1 NA NA
##
## [[3]]
## # A tibble: 1 × 7
## X1 X2 X3 X4 X5 X6 X7
## <lgl> <lgl> <lgl> <lgl> <lgl> <lgl> <lgl>
## 1 NA NA NA NA NA NA NA
##
## [[4]]
## # A tibble: 1 × 3
## X1 X2 X3
## <lgl> <lgl> <lgl>
## 1 NA NA NA
##
## [[5]]
## # A tibble: 25 × 17
## 時 `気圧(hPa)` `気圧(hPa)` `降水量(mm)` `気温(℃)` `露点温度(℃)`
## <chr> <chr> <chr> <chr> <chr> <chr>
## 1 時 現地 海面 降水量(mm) 気温(℃) 露点温度(℃)
## 2 1 1007.0 1009.7 -- 28.7 23.2
## 3 2 1006.5 1009.2 -- 28.7 23.2
## 4 3 1006.9 1009.6 -- 28.5 23.2
## 5 4 1006.7 1009.4 -- 28.2 23.1
## 6 5 1007.0 1009.7 -- 27.8 23.4
## 7 6 1007.3 1010.0 -- 28.0 23.6
## 8 7 1007.4 1010.1 -- 29.6 23.6
## 9 8 1007.6 1010.3 -- 31.2 23.8
## 10 9 1007.5 1010.2 -- 32.5 23.5
## # ℹ 15 more rows
## # ℹ 11 more variables: `蒸気圧(hPa)` <chr>, `湿度(%)` <chr>,
## # `風向・風速(m/s)` <chr>, `風向・風速(m/s)` <chr>, `日照時間(h)` <chr>,
## # `全天日射量(MJ/㎡)` <chr>, `雪(cm)` <chr>, `雪(cm)` <chr>, 天気 <chr>,
## # 雲量 <chr>, `視程(km)` <chr>
##
## [[6]]
## # A tibble: 1 × 1
## X1
## <chr>
## 1 --
##
## [[7]]
## # A tibble: 1 × 4
## X1 X2 X3 X4
## <chr> <chr> <chr> <chr>
## 1 利用される方へ よくある質問(FAQ) 気象観測統計の解説 年・季節・各月の天候
tblをプリントし,どのテーブルに必要なデータが格納されているか確認すると5番目のテーブル(リスト)にあることが分かる。 次で5番目のリストを取り出す。すべてテキストデータ(chr)になっている。
d0 <- as.data.frame(tbl[[5]])
str(d0)
## 'data.frame': 25 obs. of 17 variables:
## $ 時 : chr "時" "1" "2" "3" ...
## $ 気圧(hPa) : chr "現地" "1007.0" "1006.5" "1006.9" ...
## $ 気圧(hPa) : chr "海面" "1009.7" "1009.2" "1009.6" ...
## $ 降水量(mm) : chr "降水量(mm)" "--" "--" "--" ...
## $ 気温(℃) : chr "気温(℃)" "28.7" "28.7" "28.5" ...
## $ 露点温度(℃) : chr "露点温度(℃)" "23.2" "23.2" "23.2" ...
## $ 蒸気圧(hPa) : chr "蒸気圧(hPa)" "28.4" "28.4" "28.4" ...
## $ 湿度(%) : chr "湿度(%)" "72" "72" "73" ...
## $ 風向・風速(m/s) : chr "風速" "3.8" "4.8" "5.2" ...
## $ 風向・風速(m/s) : chr "風向" "南" "南" "南" ...
## $ 日照時間(h) : chr "日照時間(h)" "" "" "" ...
## $ 全天日射量(MJ/㎡): chr "全天日射量(MJ/㎡)" "" "" "" ...
## $ 雪(cm) : chr "降雪" "×" "×" "×" ...
## $ 雪(cm) : chr "積雪" "×" "×" "×" ...
## $ 天気 : chr "天気" "" "" "" ...
## $ 雲量 : chr "雲量" "" "" "2" ...
## $ 視程(km) : chr "視程(km)" "" "" "20.0" ...
日時などの情報追加,変数の型指定,データの整形を行い,書込用テーブルを作成する。 日時のフォーマット(%Y-%m-%d %H:%M:%Sなど)はどのプログラミング言語でもほぼ共通なのでしっかりと記憶しておくこと。Rコンソールで「?strptime」とタイプすれば他の記号も調べることができる。
# 日時整形
hour <- d0[-1, '時'] # 1列目は時刻1~24(-1:一行目は不要なため削除)
# コンピュータの世界(POSIX準拠)では24時は存在しないので0~23にする必要がある。
# コンピュータ上では24時は翌日の日付になる。
datetime <- as.POSIXlt(paste(lt, hour)) # 例)2022-08-10 24
# 自動で時刻が0~23に変換される。
# 書込用テーブル作成
d1 <- data.frame(site.id = as.integer(site$id), # 整数型
site.name = site$name,
datetime = paste(datetime),
temp = as.double(d0[-1, 5]), # 倍精度浮動小数点型
wind = d0[-1, 10])
str(d1)
## 'data.frame': 24 obs. of 5 variables:
## $ site.id : int 47662 47662 47662 47662 47662 47662 47662 47662 47662 47662 ...
## $ site.name: chr "Tokyo" "Tokyo" "Tokyo" "Tokyo" ...
## $ datetime : chr "2022-08-10" "2022-08-10" "2022-08-10" "2022-08-10" ...
## $ temp : num 28.7 28.7 28.5 28.2 27.8 28 29.6 31.2 32.5 33.4 ...
## $ wind : chr "南" "南" "南" "南" ...
library(duckdb)
# データベース接続
#con <- dbConnect(duckdb(), DB) # インメモリデータベース
con <- dbConnect(duckdb("test.duckdb"), DB) # 「test」というデータベース接続/新規作成
# 既存テーブル削除(必要に応じて実施)
dbSendQuery(con, paste('DROP TABLE IF EXISTS', site$name))
# テーブル追記書込
dbWriteTable(con, site$name, d1, append = T)
# データ選択(ちゃんと保存されたか確認すること)
res <- dbSendQuery(con, 'SELECT * FROM Tokyo')
# 選択結果取得
dbFetch(res)
## site.id site.name datetime temp wind
## 1 47662 Tokyo 2022-08-10 28.7 南
## 2 47662 Tokyo 2022-08-10 28.7 南
## 3 47662 Tokyo 2022-08-10 28.5 南
## 4 47662 Tokyo 2022-08-10 28.2 南
## 5 47662 Tokyo 2022-08-10 27.8 南
## 6 47662 Tokyo 2022-08-10 28.0 南
## 7 47662 Tokyo 2022-08-10 29.6 南
## 8 47662 Tokyo 2022-08-10 31.2 南南西
## 9 47662 Tokyo 2022-08-10 32.5 南南西
## 10 47662 Tokyo 2022-08-10 33.4 南南西
## 11 47662 Tokyo 2022-08-10 34.3 南南西
## 12 47662 Tokyo 2022-08-10 34.3 南南西
## 13 47662 Tokyo 2022-08-10 34.3 南
## 14 47662 Tokyo 2022-08-10 33.8 南
## 15 47662 Tokyo 2022-08-10 33.6 南
## 16 47662 Tokyo 2022-08-10 32.6 南
## 17 47662 Tokyo 2022-08-10 31.3 南南西
## 18 47662 Tokyo 2022-08-10 30.2 南
## 19 47662 Tokyo 2022-08-10 29.6 南
## 20 47662 Tokyo 2022-08-10 28.7 南南東
## 21 47662 Tokyo 2022-08-10 28.5 南
## 22 47662 Tokyo 2022-08-10 28.7 南
## 23 47662 Tokyo 2022-08-10 28.2 南
## 24 47662 Tokyo 2022-08-10 28.1 南
# 選択結果解放
dbClearResult(res)
# データベース接続解除
dbDisconnect(con, shutdown = T)
# 既存ファイル削除(必要に応じて実施)
file.remove(F.O)
## [1] TRUE
# テーブル追記書込
# (macOSXはコマンド:brew install libomp で
# Open MPという並列計算ライブラリをインストールしておく)
library(data.table)
fwrite(d1, file = F.O, sep = ',', append = T)
# 読込確認
(d2 <- fread(file = F.O))
## site.id site.name datetime temp wind
## <int> <char> <IDat> <num> <char>
## 1: 47662 Tokyo 2022-08-10 28.7 南
## 2: 47662 Tokyo 2022-08-10 28.7 南
## 3: 47662 Tokyo 2022-08-10 28.5 南
## 4: 47662 Tokyo 2022-08-10 28.2 南
## 5: 47662 Tokyo 2022-08-10 27.8 南
## 6: 47662 Tokyo 2022-08-10 28.0 南
## 7: 47662 Tokyo 2022-08-10 29.6 南
## 8: 47662 Tokyo 2022-08-10 31.2 南南西
## 9: 47662 Tokyo 2022-08-10 32.5 南南西
## 10: 47662 Tokyo 2022-08-10 33.4 南南西
## 11: 47662 Tokyo 2022-08-10 34.3 南南西
## 12: 47662 Tokyo 2022-08-10 34.3 南南西
## 13: 47662 Tokyo 2022-08-10 34.3 南
## 14: 47662 Tokyo 2022-08-10 33.8 南
## 15: 47662 Tokyo 2022-08-10 33.6 南
## 16: 47662 Tokyo 2022-08-10 32.6 南
## 17: 47662 Tokyo 2022-08-10 31.3 南南西
## 18: 47662 Tokyo 2022-08-10 30.2 南
## 19: 47662 Tokyo 2022-08-10 29.6 南
## 20: 47662 Tokyo 2022-08-10 28.7 南南東
## 21: 47662 Tokyo 2022-08-10 28.5 南
## 22: 47662 Tokyo 2022-08-10 28.7 南
## 23: 47662 Tokyo 2022-08-10 28.2 南
## 24: 47662 Tokyo 2022-08-10 28.1 南
## site.id site.name datetime temp wind
野球データ取得の例
url <- 'https://baseball-data.com/stats/hitter-all/'
library(httr)
url |> GET(timeout(30)) |> read_html() |> html_table() -> tbl
tbl
## [[1]]
## # A tibble: 56 × 21
## 順位 選手名 チーム 打率 試合 打席数 打数 安打 本塁打 打点 盗塁 四球
## <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 1 近藤 … ソフ… .337 46 195 166 56 6 24 4 28
## 2 2 田宮 … 日本… .333 39 143 123 41 1 19 4 11
## 3 3 サンタナ ヤク… .317 48 188 167 53 7 27 1 18
## 4 4 長岡 … ヤク… .309 48 195 175 54 3 15 1 12
## 5 4 村松 … 中日 .309 42 153 136 42 0 10 3 12
## 6 6 小園 … 広島 .306 45 188 173 53 1 23 3 12
## 7 7 柳田 … ソフ… .298 46 204 168 50 4 34 3 31
## 8 8 宮﨑 … DeNA .296 47 174 152 45 3 16 0 21
## 9 9 福田 … オリ… .293 41 162 140 41 1 10 6 17
## 10 10 秋山 … 広島 .288 43 174 163 47 1 10 2 9
## # ℹ 46 more rows
## # ℹ 9 more variables: 死球 <chr>, 三振 <chr>, 犠打 <chr>, 併殺打 <chr>,
## # 出塁率 <chr>, 長打率 <chr>, OPS <chr>, RC27 <chr>, XR27 <chr>
d.b <- as.data.frame(tbl[[1]])
library(DT)
datatable(d.b)
食べログの池袋ラーメンデータ取得の例
library(tidyverse)
lines <- readLines('https://tabelog.com/tokyo/A1305/A130501/R607/rstLst/ramen/')
names <- prs <- infos <- NULL
for (i in seq_along(lines))
{
# 店舗名を取得
is.name <- str_detect(string = lines[i],
pattern = "list-rst__rst-name")
if ( is.name )
{
lines[i + 1] |> str_replace_all(pattern = "\\<.*?\\>", replacement = "") |> str_trim() -> name
if ( name != "" ) names <- c(names, name)
}
# 店舗情報を取得
is.info <- str_detect(string = lines[i],
pattern = "list-rst__area-genre cpy-area-genre")
if ( is.info )
{
lines[i] |> str_replace_all(pattern = "\\<.*?\\>", replacement = "") |> str_trim() |>
str_replace_all(pattern = "ラーメン・つけ麺", replacement = "") |>
str_replace_all(pattern = "\\(|\\)", replacement = "") -> info
infos <- c(infos, info)
}
# PRコメントを取得
is.pr <- str_detect(string = lines[i],
pattern = "list-rst__comment list-rst__comment--hover js-open-review-window")
if ( is.pr )
{
lines[i + 1] |> str_replace_all(pattern = "\\<.*?\\>", replacement = "") |> str_trim() -> pr
prs <- c(prs, pr)
}
}
d1 <- cbind(店舗名 = names, 店舗情報 = infos, PRコメント = prs)
library(DT)
datatable(d1)
次をおこなうRソースファイル(拡張子:R)を作成,プログラム実行しデータベースを作成せよ。また,値が無いところはどうすれば良いか検討せよ。 forループを使いデータを取得する。
2021年12月31日~2022年1月1日までの気象データ(気温,湿度,日照時間,風向)をデータベースに格納する。
【重要】プログラムで連続してデータ収集する場合は,ウェブサーバーの負荷軽減のため,プログラムを一定時間休止させるコマンド:Sys.sleep(runif(1, min = 1, max = 2))をループ内に置き1~2秒の間隔でデータを取得すること。
ヒント:
t.fr <- as.POSIXlt('2021-12-30')
t.to <- as.POSIXlt('2022-01-01')
ts <- as.POSIXlt(seq(t.fr, t.to, by = 'days'))
ts
## [1] "2021-12-30 JST" "2021-12-31 JST" "2022-01-01 JST"