Rを使ってYahoo!乗換案内から運賃や所要時間,乗換回数を取得するコード書いた

(※追記あり)
数日前にスクレイピングという言葉を学び,とりあえずRでやってみた.まだまだ勉強途中なのでいろいろ間違ってるかもしれないし,計算速度もそんなに速くないのだけど,どなたか詳しい人ご教授ください.むしろもっと楽な方法があれば是非!

内容はYahoo!乗換案内に対して,出発駅と到着駅を与えることで,運賃,所要時間,乗換回数を取ってくるコードになっていて,複数提示されるうちの1番上を取ってきます.最終的にデータフレームに閉まって終了という内容です.途中のHTML解析部分はひどく手作業感が出ているので,正規表現などを使ってもう少しスマートな感じにしたいのですが,とりあえず動くことは動きます.こんなデータを取得したい需要は一部の分野にしかなさそうですが,そこはご愛敬.動かす場合は最初にRCurlのpackageをインストールする必要があります.計算速度は1000組合せで30分程度.うーむ.下の例では簡単のため,20組合せ駅にしてあります.

library(RCurl)
#出発駅と到着駅ベクトル
station1 <- c("渋谷","表参道", "外苑前", "青山一丁目", "赤坂見附", "溜池山王", "虎ノ門", "新橋", "銀座", 
"京橋", "日本橋", "三越前", "神田", "末広町", "上野広小路", "上野", "稲荷町", "田原町", "浅草", "渋谷")
station2 <- c("品川","大崎", "五反田", "目黒", "恵比寿", "渋谷", "原宿", "代々木", "新宿", "新大久保", 
"高田馬場", "目白", "池袋", "大塚", "巣鴨", "駒込", "田端", "西日暮里", "日暮里", "鶯谷")
hh <- length(station1)

#URLエンコードしておく
station3 <- numeric(hh)
station4 <- numeric(hh)
for(i in 1:hh){
  station3[i] <- paste(c("",charToRaw(iconv(station1[i],"CP932","UTF-8"))),collapse="%")
  station4[i] <- paste(c("",charToRaw(iconv(station2[i],"CP932","UTF-8"))),collapse="%")
}

Cost.v <- numeric(hh)
Time.in <- numeric(hh)
Time.out <- numeric(hh)
Transfer.v  <- numeric(hh)

#yahoo乗換案内から取ってくる
system.time(
for(i in 1:hh){

  eval(parse(text=paste("txt <- getURL(\"http://transit.map.yahoo.co.jp/search/result?from=", station3[i], "&to=", station4[i], 
  "&via=&shin=1&ex=1&al=1&hb=1&lb=1&sr=1&ym=201008&d=26&hh=12&m1=4&m2=5&type=1&ws=2&s=0&ost=0&ei=utf-8&x=0&y=0&kw=", 
  station4[i], "\")",sep="",collapse="")))
    ##抽出
    a1 <- regexpr("<dl><dt>運賃:</dt><dd>片道<span class=\"route-fare-", txt)
    a2 <- regexpr("円</span><!--\n-->", txt)
    a3 <- regexpr("</span>時間:</dt><dd>", txt)
    a4 <- regexpr("</dd></dl>\n<!--heikin-->", txt)
    a5 <- regexpr("<dl><dt>乗り換え:</dt><dd><span class=\"route-transfer-", txt)
    a6 <- regexpr("</span></dd></dl>\n</div><!--end infomation2 -->", txt)

    #運賃
    cost <- substr(txt, a1+46, a2)
    a10 <- regexpr("円", cost)
      if(regexpr("on", cost)==1){
        Cost.v[i] <- substr(cost, 5, a10-1)
        if(regexpr(",", Cost.v[i])!=-1){
          Cost.v[i] <- paste(substr(Cost.v[i],1,regexpr(",", Cost.v[i])-1), substr(Cost.v[i],regexpr(",", Cost.v[i])+1, 20), sep="")
        }
      }else{
        Cost.v[i] <- substr(cost, 6, a10-1)
        if(regexpr(",", Cost.v[i])!=-1){
          Cost.v[i] <- paste(substr(Cost.v[i],1,regexpr(",", Cost.v[i])-1), substr(Cost.v[i],regexpr(",", Cost.v[i])+1, 20), sep="")
        }
      }

    #所要時間
    time <- substr(txt, a3+19, a4-1)
    a7 <- regexpr("分(乗車", time)
    Time.in[i] <- substr(time, 1, a7-1)
      if(regexpr("時間", Time.in[i])!=-1){
        Time.in[i] <- as.numeric(substr(Time.in[i], 1, 1))*60 +  as.numeric(substr(Time.in[i], 4, 10))
      }

    a8 <- regexpr("分、ほか", time)
    a9 <- regexpr("分)", time)
    Time.out[i]<- substr(time, a8+4, a9-1)

    #乗換回数
    transfer <- substr(txt, a5+50, a6-1)
    a11 <- regexpr("回", transfer)
    if(regexpr("on", transfer)==1){
      Transfer.v[i] <- substr(transfer, 5, a11-1)
    }else{
      Transfer.v[i] <- substr(transfer, 6, a11-1)
    }
    print(i) #どこまで進んだか見たいとき用
})

Cost.v <- as.numeric(Cost.v)
Time.in <- as.numeric(Time.in)
Time.out<- as.numeric(Time.out)
Transfer.v <- as.numeric(Transfer.v)

Data2 <- data.frame(Origin=station1, Destination=station2, Cost=Cost.v, Timein=Time.in, Timeout=Time.out, Transfer=Transfer.v)

結果は次のように表示されます.

    Origin Destination Cost Timein Timeout Transfer
1        渋谷     品川  160     12       0        0
2      表参道     大崎  310     16       6        1
3      外苑前   五反田  310     17       6        1
4  青山一丁目     目黒  310     15       4        1
5    赤坂見附   恵比寿  290     14       4        1
6    溜池山王     渋谷  160     10       0        0
7      虎ノ門     原宿  290     18       4        1
8        新橋   代々木  210     20       4        1
9        銀座     新宿  310     20       6        1
10       京橋 新大久保  350     27       8        2
11     日本橋 高田馬場  190     15       0        0
12     三越前     目白  320     26       9        2
13       神田     池袋  320     20       5        1
14     末広町     大塚  320     22       7        1
15 上野広小路     巣鴨  210     17       8        1
16       上野     駒込  150      8       0        0
17     稲荷町     田端  310     13       5        1
18     田原町 西日暮里  290     13       5        1
19       浅草   日暮里  290     13       5        1
20       渋谷     鶯谷  190     28       0        0

それはともかくとして,このスクレイピングってどこまでやっていいものなのでしょう….Librahackの二の舞にはなりたくないよ.

(※追記)
id:kkobayashi_aさんが大幅に綺麗に書き直してくださいました.感謝多謝!
id:kkobayashi_a:20100829:p1