不動産をスクレイピングして最高の物件を得る

この記事は[ZOZOテクノロジーズ #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/zozo_tech2) 6日目の記事になります。
昨日は[YasuhiroKimesawa](https://qiita.com/YasuhiroKimesawa)さんの[【BtoBシステムあるある】特別対応する場合のモデルについて考察する](https://qiita.com/YasuhiroKimesawa/items/bac3eea2bfc3f6d68458)でした。
とてもあるあるなケースを示してくれていて共感できる記事です。

# 背景
引越をしたいと思っていてもなかなか良い物件に巡り会えないことがあります。
巡り会えたとしても、すでに申し込みがあり契約まで進めなかったりします。
私の望んでいた地域では物件情報が掲載された次の日には申し込みが入っているような激戦地でした。

今回はそんな激戦地でも最高の物件を得るために作ったものを紹介します。

# 作ったもの
作ったものは「特定のサイトをスクレイピングして、新しい物件があったら通知するシステム」です。
定期的に関数を実行できる環境さえあれば良いので、[GAS](https://developers.google.com/gsuite/aspects/appsscript?hl=ja)で動くようにしました。

実装の内容は下記の3つに分けて紹介します。

1. 物件のスクレイピング
2. 通知済み物件の保存
3. 新しい物件の通知

# 1. 物件のスクレイピング
まずは物件の情報を得るためにスクレイピングを行います。
スクレイピングする対象のサイトは、気になっている地域の不動産を探して選定してください。
※ スクレイピングは[用法用量](https://qiita.com/nezuq/items/c5e827e1827e7cb29011)を守って行いましょう

スクレイピングする処理を抜粋します。

```js

var url = SCRAPING_TARGET_URL;
var html = UrlFetchApp.fetch(url).getContentText('UTF-8');

var imagePathes = Parser.data(html)
  .from('<span class="img_area" style="background-image:url(')
  .to(')"></span>')
  .iterate();
```

スクレイピングはGASのライブラリである[Parser](https://www.kutil.org/2016/01/easy-data-scrapping-with-google-apps.html)を使っています。
Parserは開始と終了の文字列を指定して、その間の値を取得するシンプルなパーサーです。
上記の例では物件の画像のパスを得るためにHTMLタグを指定しています。
`iterate`関数を用いると、指定したページ内を繰り返し探索してくれます。


# 2. 通知済み物件の保存
新しい物件のみを通知するためには、どの物件を通知したかを覚えておかなくてはいけません。
そこで今回は物件情報のDBとしてGoogleスプレッドシートを用いました。

Googleスプレッドシートにはスクレイピングした物件の画像や物件名などを保存しておき、
新しく取得してきた物件が保存されていなければ通知する形となります。
コードにするとこんな感じです。

```js
var spreadsheet = SpreadsheetApp.openById(SHEET_ID);
var sheet = spreadsheet.getSheetByName(SHEET_NAME);
var lastRow = sheet.getLastRow(); // 保存されている最後の行を取得

var oldImageUrls = [];
for(var i = 0; i <= imagePathes.length + 10; i++){
  var oldTargetRow = lastRow - i + 1;
  if (oldTargetRow < 1) {
    break;
  }

  // 最後の行から順番に、保存済みの物件の画像URLを配列に格納
  oldImageUrls.push(sheet.getRange('H' + oldTargetRow).getValue());
}

for(var i = imagePathes.length - 1; i >= 0; i--){
  var imageUrl = 'https://hogehoge.jp/' + imagePathes[i];

  if(oldImageUrls.indexOf(imageUrl) == -1){
    // スクレイピングした物件のURLが、保存されているURLに含まれていなければ
    // 通知する処理を実行する
  }
}
```

# 3. 新しい物件の通知
私はSlackとLINEに通知するようにしました。
Slackは[Block](https://api.slack.com/block-kit)で、LINEは[Flex Message](https://developers.line.biz/ja/docs/messaging-api/using-flex-messages/)で投稿しています。
もともとSlackだけだったのですが、家族も見るとなるとやはりLINEですね。

| Slack| Line |
| --- | --- |
| <img src="https://raw.githubusercontent.com/sesta/assets/master/images/scraping-estate/post-slack.png" width="320"> | <img src="https://raw.githubusercontent.com/sesta/assets/master/images/scraping-estate/post-line.png" width="240"> |

実装はGitHubで公開していますので、そちらを参照してください。
* Slackの通知の実装（ https://github.com/sesta/scraping-estate/blob/master/main.gs#L68 ）
* LINEの通知の実装（ https://github.com/sesta/scraping-estate/blob/master/main.gs#L148 ）


# まとめ
この通知のおかげで、私は無事に物件を契約することができました。
掲載された30分後に連絡して即申し込みできるのは強いですね。

私の場合は全て通知するようにしましたが、この辺りを実装するとより最高の物件を得られると思います。

* 特定の料金、部屋の大きさのみ通知
* 複数のサイトをスクレイピング

明日は、[pakio](https://qiita.com/pakio)さんの「Spring Data Elasticsearch vs Rest High Level Client (ドキュメント操作系)」です。お楽しみに！


# 参考
* [GitHub | ソースコード](https://github.com/sesta/scraping-estate)
* [Qiita | Google Apps Script(GAS)を使ったwebスクレイピング](https://qiita.com/takaito0423/items/259097b55b026800c875)
* [Qiita | Webスクレイピングの注意事項一覧](https://qiita.com/nezuq/items/c5e827e1827e7cb29011)
* [Line Developers | Messaging APIの概要](https://developers.line.biz/ja/docs/messaging-api/overview/)