都民住宅攻略法(上級編) – お台場に住んでみようとした記録 10
狙った都民住宅の空室を誰よりも早く知る方法。
僕は物件をチェックするプログラムを書くことにした。
ほんのちょっとのスキルが必要な上級編だ。
JKK Watcherは10分に1回のチェックだ。
これは全ての物件をチェックしているからで、
大量のアクセスでサイトに負荷をかけないような配慮だろう。
ここに付け入る隙がある。
数件の物件のチェックであれば、
1分に1度くらいの頻度だと迷惑をかけることもないはず。
また、空室が出たことをいち早く知るためには、
Twitterやメールだと気が付くのに時間がかかる。
そこでSMSでの通知を行うことに決めた。
SMSについてはAmazon SNSでのSMSの送信は、
日本リージョンでは対応していないようだった。
そこで、TwilioのAPIを利用しての、
自動送信を試してみることにした。
電話番号の取得方法については、こちらのサイトが参考になった。
半日程で作ったコードはこんな感じだ。
わずか197行で実装できる。
(「/home/ec2-user/jkk_checker/」ディレクトリ以下に設置することを前提)
※2016.08.03追記、同一物件に複数の空室がある場合に取得できないエラーを修正
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
#!/usr/bin/ruby require 'net/http' require 'openssl' require 'cgi' require 'nkf' require 'csv' account_sid = "ここにTwilioのACCOUNT SID" auth_token = "ここにTwilioのAUTH TOKEN" from_tel = "+1~などの表記で送信元電話番号" to_tel = "+81~の表記で受信電話番号番号" search_names = [ "トミンタワーダイバイチバンガイ", "トミンタワーダイバサンバンガイ", "トミンハイムダイバサンバンガイ", "トミンハイムダイバゴバンガイ" ] def twilio_request(account_sid, auth_token, from_tel, to_tel, body) http = Net::HTTP.new("api.twilio.com", 443) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE post_params = { "From" => from_tel, "To" => to_tel, "Body" => body } req = Net::HTTP::Post.new("/2010-04-01/Accounts/" + account_sid + "/Messages") req.basic_auth account_sid, auth_token req.set_form_data(post_params) response = http.request(req) return response end def http_request(path, method = "GET", post_string = nil, headers = nil) http = Net::HTTP.new("jhomes.to-kousya.or.jp", 443) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE response = nil if method.upcase == "POST" response = http.post(path, post_string, headers) elsif method.upcase == "GET" response = http.get(path, headers) end return response end room_name_hash = Hash.new() if File.exist?("/home/ec2-user/jkk_checker/room.csv") CSV.foreach("/home/ec2-user/jkk_checker/room.csv") do |row| room_name_hash[row[0] + "," + row[1]] = row[0] + " " + row[1] + " " + row[2] end end rooms = Array.new() for search_name in search_names jsession_id = nil response = http_request("/search/jkknet/service/akiyaJyoukenStartInit") if response.key?("Set-Cookie") if response["Set-Cookie"] =~ /JSESSIONID=([^;]+);/ jsession_id = $1 end end headers = { "Referer" => "https://jhomes.to-kousya.or.jp/search/jkknet/service/akiyaJyoukenStartInit" } unless jsession_id == nil headers["Cookie"] = "JSESSIONID=" + jsession_id end response = http_request("/search/jkknet/service/akiyaJyoukenStartInit", "POST", "redirect=true&url=" + CGI.escape("https://jhomes.to-kousya.or.jp/search/jkknet/service/akiyaJyoukenStartInit"), headers) body = NKF.nkf("--ic=cp932 --oc=UTF-8", response.body) token = "" abcde = "" if body =~ /<input type="hidden" name=token value="([^"]*)">/ token = $1 end if body =~ /<input type="hidden" name="abcde" value="([^"]*)">/ abcde = $1 end post_string = "akiyaInitRM.akiyaRefM.yachinFrom=0&akiyaInitRM.akiyaRefM.yachinTo=999999999&akiyaInitRM.akiyaRefM.mensekiFrom=0&akiyaInitRM.akiyaRefM.mensekiTo=9999.99&akiyaInitRM.akiyaRefM.yusenBoshu=&akiyaInitRM.akiyaRefM.jyutakuKanaName=" + CGI.escape(NKF.nkf("--ic=UTF-8 --oc=cp932", search_name)) + "&akiyaInitRM.akiyaRefM.ensenCd=&akiyaInitRM.akiyaRefM.mskKbn=&token=" + token + "&abcde=" + abcde + "&jklm=E17511BF89D3A101AFEF10EBF1587561&sen_flg=1&akiyaInitRM.akiyaRefM.checks=&akiyaInitRM.akiyaRefM.allCheck=&akiyaInitRM.akiyaRefM.madoris=&akiyaInitRM.akiyaRefM.tanshinFlg=&akiyaInitRM.akiyaRefM.teishiKaiFlg=&akiyaInitRM.akiyaRefM.yuguFlg=&sen_flg=1" response = http_request("/search/jkknet/service/akiyaJyoukenRef", "POST", post_string, headers) body = NKF.nkf("--ic=cp932 --oc=UTF-8", response.body) list_array = body.split(/<table width="713" border="0" cellpadding="\d" cellspacing="1" class="cell666666">/) body_array = Array.new() if list_array.count == 2 if body =~ /<input type="hidden" name=token value="([^"]*)">/ token = $1 end if body =~ /<input type="hidden" name="abcde" value="([^"]*)">/ abcde = $1 end list_array2 = body.split(/<tr class="ListTXT\d+">/) list_array2.shift for list_body in list_array2 if list_body =~ /<a href="" onclick="javascript:senPage\('','([^']+)','(\d+)','(\d+)'\); return false"><img src="\/search\/jkknet\/images\/bt2_syousai\.gif" alt="詳細"/ post_string = "akiyaRefRM.akiyaDatM.boshuNo=&akiyaRefRM.akiyaDatM.mskKbn=" + $1 + "&akiyaRefRM.akiyaDatM.jyutakuCd=" + $2 + "&akiyaRefRM.akiyaDatM.yusenKbn=" + $3 + "&akiyaRefRM.showCount=10&token=" + token + "&abcde=" + abcde + "&jklm=26131A06F36B4487BA38B2958068CA6B&pagingInputDataGrid_url=AKIYA&pagingInputDataGrid_scope=session&pagingInputDataGrid_name=AKIYA_GRID&pagingInputDataGrid_id=0&sen_flg=1" response = http_request("/search/jkknet/service/akiyaSenDet", "POST", post_string, headers) body = NKF.nkf("--ic=cp932 --oc=UTF-8", response.body) body_array.push(body) end end else body_array.push(body) end for body in body_array array = body.split(/<table width="807" border="0" cellpadding="\d" cellspacing="1" class="cell666666">/) if array.count == 3 body2 = array[2] array2 = body2.split(/<\/table>/) body3 = array2[0] array3 = body3.split(/<\/tr>/) if array3.count > 3 array3.shift array3.shift i = 0 for body4 in array3 body4.gsub!(/(\s|\r|\n)*<tr [^>]+>(\s|\r|\n)*<td/m, "<td") if i % 2 == 0 array4 = body4.split(/<\/td>/) if array4[0] =~ /<td [^>]+>([^<]+)/m room = { "search_name" => search_name, "room_name" => $1.sub(/^(\s|\r|\n)*/,"").sub(/(\s|\r|\n)*$/,"") } if array4[2] =~ /<a [^>]+>([^<]+)/m room["madori"] = $1.sub(/^(\s|\r|\n)*/,"").sub(/(\s|\r|\n)*$/,"") else room["madori"] = "不明" end rooms.push(room) end end i += 1 end end end end end f = open("/home/ec2-user/jkk_checker/room.csv", "w") for room in rooms f.puts '"' + room["search_name"].gsub(/"/, '""') + '","' + room["room_name"].gsub(/"/, '""') + '","' + room["madori"].gsub(/"/, '""') + '"' if !room_name_hash.key?(room["search_name"] + "," + room["room_name"]) #puts room["room_name"] response = twilio_request(account_sid, auth_token, from_tel, to_tel, room["search_name"] + " " + room["room_name"] + " " + room["madori"] + "の空室がでました。") #puts response.body else room_name_hash.delete(room["search_name"] + "," + room["room_name"]) end end f.close() room_name_hash.each_key {|key| twilio_request(account_sid, auth_token, from_tel, to_tel, room_name_hash[key] + "に申し込みがありました。") } |
こちらをcronの設定などで、1分に1度動かせばOKだ。
欲を言うと、申込みまで自動でやりたかったのだが、
さすがにそこの動作検証でJKKに迷惑をかけるわけにはいかないので、
この辺りで妥協。
待つこと一か月程。
僕は無事狙った物件の申し込みに成功した。
冒頭の写真はこの物件を内見した時のもの。
高そうな物件に思われるかもしれないが、
平米単価は都心の平均に比べるとかなり安い。
…ところが、諸事情によりこの物件をキャンセルする方向となる。
逃した魚は大きいと落胆しつつ、そういえばURがあるよね、
と、内見した後のその足で、URのお台場現地案内所に向かってみた。
URに入居するためにも、少し工夫がいる。
次回はURの攻略法について紹介しよう。