GoogleスプレッドシートをWebスクレイピングしてみた

http://ittemia.jp/zensen/themes/view/39
ここで駅名しりとりをしていて、私も機会があれば投稿したいなぁと、列車で移動中とかに時々チェックしている。一回出た駅は使えないルールなので、取りに行けそうだと思っても、今までに出てないか気になる事がある。
http://spreadsheets.google.com/pub?key=pS_Iu-LTUb202uc2p8lQoOw&gid=1
このGoogleスプレッドシートで、今までに出た駅名一覧が登録されているのだけど、携帯電話から見に行くには、フルブラウザを使わなくてはいけなかったりで、なかなか難しい。
このGoogleスプレッドシートはHTMLで表現されているので、Webスクレイピングして必要な情報だけ抜き出せば、携帯電話から見れる形に変換するのも簡単そう。そう思いついたので、さっそくやってみた。
最初はHpricotでHTML解析しようと思ったのだけど、どうもHTMLの構造がスプレッドシートの行単位で抜き出しにくいので、正規表現で行単位でマッチさせて、必要な値を取り出すことにした。
コード的には、こんな感じ。

url = 'http://spreadsheets.google.com/pub?key=pS_Iu-LTUb202uc2p8lQoOw&gid=0'
doc = open(url).read

@list = ''
rows = doc.scan(
  %r|<tr><td class=hd><p style='height:16px;'>.</td><td class='s0 ' >[^<]+<td class='s1 ' >[^<]+<td class='s1 ' >[^<]+<td class='s1 ' >[^<]+|)
rows.each do |row|
  m = row.match(
    %r|<td class='s0 ' >([^<]+)<td class='s1 ' >([^<]+)<td class='s1 ' >([^<]+)<td class='s1 ' >([^<]+)|)
  if m #@listに、駅名表示用HTMLを出力
    @list << "#{m[1]}:#{m[2]}(#{m[3]})@#{m[4]}<br />"
    doc = m.post_match
  end
end

で、かな順のシートのページも合わせて、ramazeを使ってWebアプリ化。
コードはこんな感じ。ramazeだと、これくらいのものなら1つのファイルで書けちゃうのが良いな。

require 'ramaze'
require 'open-uri'

class ShiritoriController < Ramaze::Controller
  engine :Erubis
  map '/'
  layout :layout

  INDEX = %w[あ か さ た な は ま や ら わ]

  def index
    sort
  end

  def history
    url = 'http://spreadsheets.google.com/pub?key=pS_Iu-LTUb202uc2p8lQoOw&gid=0'
    doc = open(url).read

    @list = ''
    rows = doc.scan(%r|<tr><td class=hd><p style='height:16px;'>.</td><td class='s0 ' >[^<]+<td class='s1 ' >[^<]+<td class='s1 ' >[^<]+<td class='s1 ' >[^<]+|)
    rows.each do |row|
      m = row.match(%r|<td class='s0 ' >([^<]+)<td class='s1 ' >([^<]+)<td class='s1 ' >([^<]+)<td class='s1 ' >([^<]+)|)
      if m #駅名表示
        @list << "#{m[1]}:#{m[2]}(#{m[3]})@#{m[4]}<br />"
        doc = m.post_match
      end
    end    
    return %{
履歴順 | <a href="<%= Rs(:sort) %>">かな順</a><br />
<%= @list %>
    }
  end

  def sort
    url = 'http://spreadsheets.google.com/pub?key=pS_Iu-LTUb202uc2p8lQoOw&gid=1'
    doc = open(url).read

    m = true
    @list = ''

    rows = doc.scan(%r|<tr><td class=hd><p style='height:16px;'>.</td><td class='s\d ' >[^<]*<td class='s\d ' >[^<]*<td class='s\d ' >[^<]*<td class='s\d ' >[^<]*|)
    rows.each do |row|
      # インデックス表示
      m = row.match(%r|<tr><td class=hd><p style='height:16px;'>.</td><td class='s0 ' ><td class='s1 ' >([^<]+)<td class='s2 ' ><td class='s2 ' >|)
      if m
        index_no = INDEX.index(m[1])
        @list << %!<a href="#index" name="i#{index_no}">●#{m[1]}</a><br />! if index_no
      end
      
      # 駅名表示
      m = row.match(%r|<tr><td class=hd><p style='height:16px;'>.</td><td class='s3 ' >([^<]+)<td class='s4 ' >([^<]+)<td class='s4 ' >([^<]+)<td class='s4 ' >([^<]+)|)
      if m
        @list << "#{m[1]}:#{m[2]}(#{m[3]})@#{m[4]}<br />"
      end
    end
    @index = INDEX
    return %{
<a href="<%= Rs(:history) %>">履歴順</a> | かな順<br />
<a name="index">&gt;</a>
<% @index.each_index do |i| %>
<a href="#i<%= i %>"><%== @index[i] %></a> 
<% end %><br />
<%= @list %>
    }

  end

  def layout
    %{
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>駅名しりとり 駅名一覧</title>
</head>
<body>
<h1>全国駅名しりとりラリー 携帯用駅名一覧</h1>
<%= @content %>
</body>
</html>
    }
  end
end

Ramaze.start

そして、Webに公開。
http://api.xii.jp/ittemia/shiritori/

単純なHTMLしか使ってないとは言え、携帯から見えるHTMLなのか不安だったが、とりあえず表示はできるっぽい。


と、ここまでやって気づいたが、GoogleならスプレッドシートのWeb APIを公開していてもおかしくない。
少し探してみたら、案の定公開していた。
http://code.google.com/apis/spreadsheets/overview.html
うーむ、こっちを使った方が良かったのかも。