Web::Scraperを使って、物件一覧をGoogle Mapsにマッピング

福岡ファミリー向け物件特集!: ファミリー物件を見ていて、これはいいな、RSS登録するか。

と思ったのですが、このRSSはどうやら福岡の不動産情報探し・お部屋探しサイト「ムビマップ福岡」:こだわりの物件特集[賃貸版]: 物件一覧のようで、僕にはいらん情報も混じっておりました。

で、話題のWeb::Scraperでスクレイピングしてみるか。ついでに取得した一覧はGoogle Mapsにマッピングしてやるか。
というエントリです。

http://fooo.name/ からURL一覧を取り出すのにWeb::Scraper使ったけど、これは良いね

scraper http://fooo.name/accounts/otsune
してsでソース見てそれっぽくXPath書いてdumpして、データが取れてたらcでソース出して終了。イカス。

を参考にさせてもらいました。

まずは、

$ scraper http://www.movimap.net/kodawariblog/family/

して

scraper> s

してソースをざーっと斜め読み。
取ってきたい情報のXPathを考える。

scraper> process '//div[@class="box"]/div[@class="box01"]/h3/a', 'link[]' => '@href', 'title[]' => 'TEXT'

これでどげんじゃろかと、出力してみる。

scraper> y

すると、

---
link:
  - !!perl/scalar:URI::http http://www.movimap.net/kodawariblog/2007/10/post_134.html
  - !!perl/scalar:URI::http http://www.movimap.net/kodawariblog/2007/08/post_33.html
....
title:
  - +*。+純和風♪駅が近いので通勤・通学、奥様のショッピングにも便利なんデスヨ~(人・∀・*)+。*+
  - ☆鴻巣山のふもとの静かな暮らし☆
....

おし、これはおげ。
続いて住所情報が欲しいので、

scraper> process '//div[@class="box"]/div[@class="box02"]/p', 'address[]' => 'TEXT'

そして、

scraper> y

その結果が、

---
address:
  - '■□加藤邸(桜台)  福岡県筑紫野市桜台1丁目7-11 □■ +*○。+純和風のとても素敵なお家です!広さは4LDKと広々!駐車場も一台付いていまーす +。○*+ '
  - ■□グレイス長丘 福岡市南区長丘5-8-18□■ ♪♪駐車場2台取れます♪♪
....
link:
  - !!perl/scalar:URI::http http://www.movimap.net/kodawariblog/2007/10/post_134.html
  - !!perl/scalar:URI::http http://www.movimap.net/kodawariblog/2007/08/post_33.html
....
title:
  - +*。+純和風♪駅が近いので通勤・通学、奥様のショッピングにも便利なんデスヨ~(人・∀・*)+。*+
  - ☆鴻巣山のふもとの静かな暮らし☆
....

これで、address, title, linkとそれぞれの配列にデータが入った事を確認したところで、

scraper> c

とタイプすると、

#!/usr/local/bin/perl
use strict;
use Web::Scraper;
use URI;

my $uri = URI->new("http://www.movimap.net/kodawariblog/family/");
my $scraper = scraper {
    process '//div[@class="box"]/div[@class="box02"]/p', 'address[]' => 'TEXT';
};
my $result = $scraper->scrape($uri);

とコードを自動生成してくれます!
でも、最初の方に設定したXPathが出てこないみたいなので、ひとまず、

scraper> process '//div[@class="box"]/div[@class="box02"]/p', 'address[]' => 'TEXT'; process '//div[@class="box"]/div[@class="box01"]/h3/a', 'link[]' => '@href', 'title[]' => 'TEXT';
scraper> c
#!/usr/local/bin/perl
use strict;
use Web::Scraper;
use URI;

my $uri = URI->new("http://www.movimap.net/kodawariblog/family/");
my $scraper = scraper {
    process '//div[@class="box"]/div[@class="box02"]/p', 'address[]' => 'TEXT'; process '//div[@class="box"]/div[@class="box01"]/h3/a', 'link[]' => '@href', 'title[]' => 'TEXT';
};
my $result = $scraper->scrape($uri);

とやりました。

<追記:2007-11-15 09:01>

scraper> c all

とすれば良いだけでした。miyagawaさんありがとうございました!
<追記ここまで>

後はこの生成されたコードをコピペして、適当に配列をごにょごにょして、TTへ丸投げ。

movimap_scrape.pl

#!/usr/local/bin/perl
use strict;
use warnings;
use Web::Scraper;
use Data::Dumper;
use Template;
use URI;

my $uri = URI->new("http://www.movimap.net/kodawariblog/family/");
my $scraper = scraper {
  process '//div[@class="box"]/div[@class="box01"]/h3/a',
'link[]' => '@href', 'title[]' => 'TEXT';
  process '//div[@class="box"]/div[@class="box02"]/p',
'address[]' => 'TEXT';
};
my $result = $scraper->scrape($uri);

my @list;
for my $i ( 0 .. scalar @{$result->{"address"}} - 1 ){
  my $data = $result->{"address"}->[$i];
  $data =~ s/^x{25a0}x{25a1}.*?[  d]+(.+)?x{25a1}x{25a0}.+$/$1/;
  push @list, {
    address => $data,
    link => $result->{"link"}->[$i],
    title => $result->{"title"}->[$i],
  };
}

my $tt = Template->new({
  INCLUDE_PATH => ".", EVAL_PERL => 1
});

$tt->process("webscraper_googlemap.tt", { result => @list });

webscraper_googlemap.tt

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
  <title>scraper http://www.movimap.net/kodawariblog/family/</title>
  <script src="http://maps.google.com/maps?file=api&v=2&key=***"
  type="text/javascript"></script>
  <script type="text/javascript">

  //<![CDATA[
  var lat = 33.58981
  var lng = 130.39878
  var map = null;
  var geocoder = null;

  function load() {
    if (GBrowserIsCompatible()) {
      map = new GMap2(document.getElementById("map"));
      map.addControl(new GSmallMapControl());
      map.addControl(new GMapTypeControl());
      map.setCenter(new GLatLng(lat, lng), 12);

      geocoder = new GClientGeocoder();

      [% FOREACH item = result %]
      geocoder.getLatLng(
        "[% item.address %]",
        function(point){
          if (point){
            var marker = new GMarker(point);
            map.addOverlay(marker);
            GEvent.addListener(marker, 'click', function() {
              marker.openInfoWindowHtml("<h4><a href='[% item.link %]'>[% item.title %]</a></h4>");1
            });
          }
        }
      );
      [% END %]
    }
}

//]]>
</script>
</head>
<body onload="load()" onunload="GUnload()">
  <div id="map" style="width: 80%; height: 400px"></div>
</body>

こんだけで簡単にGoogle Mapsへのマッピングが完了。
住所を抜き取る正規表現はかなりあやしいです。住所に何らかの定義をしてくれると助かるのだけどなぁ。

<追記:2007-11-15 19:18>
このへっぽこな正規表現の解決策をエントリしました。Geography::AddressExtract::Japanを使って住所抽出 jmalaさん、Yappoさんに感謝!
<追記ここまで>

出来上がったHTMLファイルはこんな感じ。

http://cgfm.jp/~cota/sandbox/webscraper_googlemap.html

僕もスクレイピング脳に少しだけ近づけた気がします。

pagetop