Yahoo路線検索スクリプト

しばらくプログラムネタを書いていなかったので、無理矢理なんか作ってみた。

よく使うWebサービスPowerShellから使えるようにすると便利なので、Yahoo路線検索をPowerShellから呼び出せるようにしてみた。

Get-Transit.ps1

param([string]$from="", [string]$to="名古屋", [DateTime]$date=[DateTime]::Now, [string]$type="ARR", [Switch]$charge, [Switch]$air)

if($from.Length -eq 0 -or $to.Length -eq 0 -or $args[0] -eq "-?") {
  $commandName = [IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
  Write-Host -foregroundColor Yellow @"

Name:
  $commandName

Description:
  Yahoo路線検索を使って、指定した駅から駅の路線を検索します。

Usage:
  $commandName [[-from] <string>] [[-to] <string>] [[-date] <DateTime>] [[-type] <string>] [[-charge] <Switch>] [[-air] <Switch>]

  -from <string>
      出発駅
        
  -to <string>
      到着駅
        
  -date <DateTime>
      探索日時と時間
        
  -type <string>
      探索方法(DEP(出発時刻指定) or ARR(到着時刻指定) or LST(終電) or NON(指定なし))
        
  -charge <Switch>
      有料特急を利用するかどうか
        
  -air <Switch>
      空路を利用するかどうか

"@
  exit 1
}
[void]([Reflection.Assembly]::LoadWithPartialName("System.Web"))

$siteEncoding = [Text.Encoding]::GetEncoding("EUC-JP")  # Yahoo路線検索サイトのエンコード

$queryParams = @{
  val_htmb="result";
  from=[Web.HttpUtility]::UrlEncode($from, $siteEncoding);    # 出発駅
  p=[Web.HttpUtility]::UrlEncode($to, $siteEncoding);         # 到着駅
  sort=0;
  num=0;
  val_yymm=$date.ToString("yyyyMM");
  val_dd=$date.ToString("dd");
  val_hh=$date.ToString("HH");
  val_m1=$date.ToString("mm")[0];
  val_m2=$date.ToString("mm")[1];
  valtimekb=$type;
  val_search=[Web.HttpUtility]::UrlEncode("探索", $siteEncoding);
}
Write-Debug  $queryParams
# 空路を利用する
if($air) { $queryParams.Add("val_dsmask_air", "AIR") }
# 有料特急を利用する
if($charge) { $queryParams.Add("val_dsmask_charge", "CHARGE") }

$webReq = [Net.HttpWebRequest]::Create(
  "http://transit.yahoo.co.jp/search?" +
  [string]::Join("&", ($queryParams.GetEnumerator() | % { "{0}={1}" -f ($_.Key, $_.Value) }))
)
$webReq.Method = "GET"
$webReq.Referer = "Mozilla/4.0 (compatible; MSIE 6.0; Windows XP)"

$webRes = $webReq.GetResponse()
$sr = New-Object IO.StreamReader($webRes.GetResponseStream(), $siteEncoding)
$content = $sr.ReadToEnd().Replace("`n", "").Replace("`r", "")
$sr.Close()
$webRes.Close()

$ptrn = New-Object Text.RegularExpressions.Regex(
  '経路[0-9]</div>(?<head>.*?)<table.*?>(?<body>.*?)view_switchers', "IgnoreCase"
)
$headPtrn = New-Object Text.RegularExpressions.Regex((
  'start.">(?<start>.*?)..</span>' +                  # 出発時間
  '.*?reach.">(?<reach>.*?)..</span>' +               # 到着時間
  '.*?result_lapse.">(?<lapse>.*?)</div>' +           # 時間
  '.*?result_distance.">.{3}(?<dist>.*?)</div>' +     # 距離
  '.*?result_passage.">.{3}(?<passage>.*?)</div>' +   # 運賃
  '.*?result_connection.">.*?>(?<con>.*?)</span>' +   # 乗り換え
  '.*?result_pass.">.{4}(?<pass>.*?)</div>'           # 定期代
    
), "IgnoreCase")
$bodyPtrn = New-Object Text.RegularExpressions.Regex((
  '<em>(?<name>.*?)</em>' +
  '(.*?<td nowrap="nowrap"><small>(?<time>.*?)</small>' +
  '.*?<td><small>(?<desc>.*?)</small>|)'
    
), "IgnoreCase")

function escape([string]$value) { [Web.HttpUtility]::HtmlDecode(($value -replace "<.*?>", "")).Trim() }

[string]::Join([Environment]::NewLine, (& {
@"
<?xml version="1.0" encoding="utf-8"?>
<transit>
"@
$ptrn.Matches($content) | % {
  $headInfo = $headPtrn.Match($_.Groups["head"].Value)
  $bodyInfo = $bodyPtrn.Matches($_.Groups["body"].Value)
    
  $startTime, $reachTime, $lapseTime =
      (escape $headInfo.Groups["start"].Value),
      (escape $headInfo.Groups["reach"].Value),
      (escape $headInfo.Groups["lapse"].Value)    
  $distance, $passage, $connection, $pass =
      $headInfo.Groups["dist"].Value,
      (escape $headInfo.Groups["passage"].Value),
      $headInfo.Groups["con"].Value,
      (escape $headInfo.Groups["pass"].Value)
@"
  <item>
      <startTime>$startTime</startTime>
      <reachTime>$reachTime</reachTime>
      <lapseTime>$lapseTime</lapseTime>
      <distance>$distance</distance>
      <passage>$passage</passage>
      <connection>$connection</connection>
      <pass>$pass</pass>
      <routes>
"@
  $bodyInfo | % {
      $name = $_.Groups["name"].Value
      $time = $_.Groups["time"].Value
      $desc = (escape $_.Groups["desc"].Value)
@"
          <route>
              <name>$name</name>
              <time>$time</time>
              <description>$desc</description>
          </route>
"@
  }
@"
      </routes>
  </item>
"@
}
@"
</transit>
"@
}))

使い方

例えば、5/31の12:00に到着時刻指定で津→名古屋への路線を検索する場合(有料特急を使う)

PS > Get-Transit 津 名古屋 "2008/5/31 12:00:00" ARR -charge

出力

<?xml version="1.0" encoding="utf-8"?>
<transit>
  <item>
      <startTime>10:59</startTime>
      <reachTime>11:53</reachTime>
      <lapseTime>54分(乗車50分、徒歩3分、ほか1分)</lapseTime>
      <distance>66.5km</distance>
      <passage>片道1,850円(乗車券980円 特別料金870円)</passage>
      <connection>0</connection>
      <pass>1か月22,260円 3か月63,450円 6か月120,210円</pass>
      <routes>
          <route>
              <name></name>
              <time>10:59〜11:49</time>
              <description>3駅 近鉄名阪乙特急</description>
          </route>
          <route>
              <name>近鉄名古屋</name>
              <time>11:50〜11:53</time>
              <description>徒歩</description>
          </route>
          <route>
              <name>名古屋</name>
              <time></time>
              <description></description>
          </route>
      </routes>
  </item>
  <item>
      <startTime>10:47</startTime>
      <reachTime>11:40</reachTime>
      <lapseTime>53分(乗車49分、徒歩3分、ほか1分)</lapseTime>
      <distance>66.5km</distance>
      <passage>片道1,850円(乗車券980円 特別料金870円)</passage>
      <connection>0</connection>
      <pass>1か月22,260円 3か月63,450円 6か月120,210円</pass>
      <routes>
          <route>
              <name></name>
              <time>10:47〜11:36</time>
              <description>3駅 近鉄名伊乙特急</description>
          </route>
          <route>
              <name>近鉄名古屋</name>
              <time>11:37〜11:40</time>
              <description>徒歩</description>
          </route>
          <route>
              <name>名古屋</name>
              <time></time>
              <description></description>
          </route>
      </routes>
  </item>
  <item>
      <startTime>10:47</startTime>
      <reachTime>11:55</reachTime>
      <lapseTime>1時間8分(乗車57分、徒歩3分、ほか8分)</lapseTime>
      <distance>73.4km</distance>
      <passage>片道2,190円(乗車券1,320円 特別料金870円)</passage>
      <connection>2</connection>
      <pass>1か月33,880円 3か月96,580円 6か月180,740円</pass>
      <routes>
          <route>
              <name></name>
              <time>10:47〜11:36</time>
              <description>3駅 近鉄名伊乙特急</description>
          </route>
          <route>
              <name>近鉄名古屋</name>
              <time>11:38〜11:41</time>
              <description>徒歩</description>
          </route>
          <route>
              <name>名鉄名古屋</name>
              <time>11:42〜11:45</time>
              <description>名鉄名古屋本線急行・中部国際空港行</description>
          </route>
          <route>
              <name>金山(愛知県)</name>
              <time>11:50〜11:55</time>
              <description>1駅 JR東海道本線・岐阜行</description>
          </route>
          <route>
              <name>名古屋</name>
              <time></time>
              <description></description>
          </route>
      </routes>
  </item>
</transit>

結果はXML形式の文字列として返ってくる。ここからDOMオブジェクトにするなり、別のオブジェクトに加工するなり自由。このスクリプトはこれ単体で使うという感じではなく、他のと組み合わせて使う感じで書いた(Webアプリから使うため用)。

例:DOMオブジェクトにしてテーブル形式で整形

PS > ([xml](Get-Transit 津 名古屋 "2008/5/31 12:00:00" ARR -charge)).transit.item | ft

startTime      reachTime      lapseTime      distance       passage        connection     pass           routes
---------      ---------      ---------      --------       -------        ----------     ----           ------
09:56          11:07          1時間11分(... 66.5km         片道980円      0              1か月22,260... routes
09:56          11:16          1時間20分(... 68.4km         片道1,150円    2              1か月31,090... routes
09:43          10:53          1時間10分(... 66.5km         片道980円      0              1か月22,260... routes

ところでYahoo路線検索のサイトに「 無断転載、複写、加工を禁ず」って書いてあるんだけど、これもまずいのかな?まずかったら消しますので、言ってください。