Pythonによる不動産情報のデータ取得&分析(1)【賃貸物件/Webスクレイピング編】
暑い日が続きますね。皆さんはいかがお過ごしでしょうか。
我が家では、不動産の賃貸で住み替えか?購入か?を検討しなくてはいけない局面が生じたので、何かデータを取得して分析できないかな、とネットサーフィンをしていたところ、発見!
【データで見る世界】機械学習を使って東京23区のお買い得賃貸物件を探してみた 〜スクレイピング編〜
ありがとうございます。このような先人の方がいらっしゃると、真似をしつつ、何とかやってみようという気がしてきます。
さて、まずは、東京23区の賃貸物件について、「データで見る世界」さんのブログを参考にして、Pythonを使って取得してみることにしましょう。「データで見る世界」さん同様にSUUMOさんからデータを取得していくにあたり、まずはSUUMO規約を確認しましょう。
私的利用を超えて使用してはいけないことは書かれていますが、それ以外に、スクレイピングに関する制限はなさそうですね。どうやらスクレイピングをしても大丈夫そうです。
不動産情報を取得する準備
まず、いつものようにrequests
モジュールとBeautifulSoup
モジュールをimportしましょう。それ以外にもtime
モジュールとpandas
モジュールもimportしておきます。
import requests from bs4 import BeautifulSoup import time import pandas as pd
SUUMOさんのホームページから「関東」「賃貸物件」と選択していくと、「関東の賃貸住宅[賃貸マンション・アパート情報探し]」のページとなり、「沿線・エリアから探す」よう選択が求められますので、「東京都」「エリア」を選びます。そうすると、「東京都ー市区郡を選択」するページが表示されます。ここで東京23区を全て選択してもいいかと思いますが、全部で20万件程度のデータになるかと思います。時間もかかるし、実行中にエラーとなる可能性も高くなりますので、区毎にデータを取得していきます。
最初は、足立区を選択してみましょう。足立区の賃貸物件検索ページのアドレスをコピペし、urlに格納します。
# SUUMO 賃貸 東京都足立区 検索 url = 'https://suumo.jp/jj/chintai/ichiran/FR301FC001/?ar=030&bs=040&ta=13&sc=13121&cb=0.0&ct=9999999&et=9999999&cn=9999999&mb=0&mt=9999999&shkr1=03&shkr2=03&shkr3=03&shkr4=03&fw2=&srch_navi=1'
いつものようにurlの情報を取得して、htmlで構文解析を行い、結果をsoupオブジェクトに格納します。そして、そのsoupオブジェクトを通じて、基本的にデータを取得していくことになります。
result = requests.get(url)
c = result.content
soup = BeautifulSoup(c, "html.parser")
実際にホームページをご覧になると分かると思いますが、物件データは数十ページに跨がっています。また、データ数によって日々、ページ数は変わってきますので、まずはページ数を取得して、その後1ページずつデータしていきます。
ページ数・urlの取得
# ページ数を取得 body = soup.find("body") pages = body.find_all("div", {'class':'pagination pagination_set-nav'}) pages_text = str(pages) pages_split = pages_text.split('</a></li>\n</ol>') num_pages = int(pages_split[0].split('>')[-1])
「データで見る世界」さんとは、自分自身で理解しやすいように、スクリプトは変更しています。 実際にデータを取得していく際には、かなり変更している部分がありますので、参考にしていただければと思います。
さて、次に、urlsリストに1ページずつ、urlを加えていきます。ここはそのまま使わさせていただいています。
# 全てのページのURLを作成 # urlを入れる箱(リスト)を設定 urls = [] # 1ページ目を格納 urls.append(url) # 2ページ目以降を格納 for i in range(num_pages-1): pg = str(i+2) url_page = url + '&pn=' + pg urls.append(url_page)
スクレイピングによるデータ取得の準備
まずは、全データを格納するリストdata
とエラーが出たときにその内容を格納するリストerrors
を用意します。賃貸物件の件別データ(1つの物件でも複数の部屋の物件が出ている場合は、その件別も含む)を1つ1つ取り込みます。その場合の件別データそのものもリスト形式なので、data
はリストのリストになります。
data = [] errors = []
さて、スクレイピングの本体部分に入っていきます。urlsに格納したurlを一つずつ取り出して、1ページ毎にスクレイピングしていきます。
for url in urls:
スクレイピングの最中にエラーが出ても、スクリプトが動き続けるように、try〜exceptを入れます。ついでにerrors
にエラーの内容、エラーが出たときのurl、何番目のデータかを保存しておきます。
try: # ここにスクリプトのエンジン部分を書きます。 except Exception as e: errors.append([e, url, len(data)]) pass
いよいよ、スクレイピングのエンジン部分に入っていきましょう。url毎にsoupオブジェクトを作り、soupオブジェクトから各種情報を取り出すようにします。
result = requests.get(url) c = result.content soup = BeautifulSoup(c, "html.parser")
Google Chromeの検証機能を使って探していくと、ページの中で件別データ全体を選択するのはdiv
タグのうち、id='js-bukkenList'
のものになります。find
メソッドの引数でこれを指定して、summaryに情報を格納します。更に、summaryに格納された情報の中で、各件別データを選択するのは、div
タグのうち、class='cassetteitem'
ですので、find_all
メソッドの引数でこれを指定して、件別データをcassetteitemsに格納します。
summary = soup.find("div",{'id':'js-bukkenList'}) cassetteitems = summary.find_all("div",{'class':'cassetteitem'})
スクレイピングのエンジン部分
その後は、cassetteitemsから件別データ(cassetteitem)毎に情報を取り出していきます。この段階でもエラー処理を行えるようにしております。
for cas in cassetteitems: try: # 情報取得用の箱を準備します。 new = '' # 新着 subtitle = '' # 物件名 location = '' # 住所 station_list = [] # 最寄駅(リスト) yrs = '' # 築年数 heights = '' # (建物の)階数 floor = '' # (物件のある)階数 rent = '' # 家賃 admin = '' # 管理費 others = '' # その他(敷金/礼金等) floor_plan = '' # 間取り area = 0 # 面積 # 物件名 subtitle = cas.find("div",{"class":"cassetteitem_content-title"}).string # 住所 location = cas.find("li",{"class":"cassetteitem_detail-col1"}).string # 最寄駅 sta = cas.find("li", {"class":"cassetteitem_detail-col2"}) stas = sta.find_all("div", {"class":"cassetteitem_detail-text"}) for s in stas: station_list.append(s.string) # 築年数、階数 col3 = cas.find("li",{"class":"cassetteitem_detail-col3"}) yrs = col3.find_all('div')[0].string heights = col3.find_all('div')[1].string tbodies = cas.find_all('tbody') for tbody in tbodies: cols = tbody.find_all('td') for i, col in enumerate(cols): if i == 0: try: new = col.span.string.strip('\r\t\n') except: new = '' if i == 2: floor = col.string if i == 3: rent = col.string if i == 4: admin = col.string if i == 5: others = col.string if i == 6: floor_plan = col.string if i == 7: area = float(col.contents[0].split('m')[0]) data.append([new, subtitle, location, station_list[0], station_list[1], station_list[2], yrs, heights, floor, rent, admin, others, floor_plan, area]) except Exception as e: errors.append([e, url,len(data)]) pass time.sleep(1) # スクレイピングする際の礼儀として、1秒待ちましょう
これで全ての物件データが取得でき、dataとerrorsに格納されました。
pandasを使ってデータをcsvファイルとして保存
さて、これらのデータをpandas
のメソッドを利用してDataFrame
オブジェクトに変換、その後、csvファイルとして保存します。
# data listを DataFrameに変換 df = pd.DataFrame(data, columns=['新着','物件名','住所','立地1','立地2','立地3','築年数','階数','物件階','家賃','管理費','敷金礼金','間取り','面積']) # csvファイルとして保存 df.to_csv('data/suumo_adachi.csv', sep = ',',encoding='utf-8') # ついでに errors fileも保存 df_errors = pd.DataFrame(errors) df_errors.to_csv('data/errors_adachi.csv', sep = ',', encoding='utf-8')
以上でデータが保存できました。念のため、errorsを開いて、エラーがないことを確認しましょう。たまに、通信環境等によってか、エラーが発生することがありますが、少ない時は無視します。エラーが多い時だけ、時間を空けて再度トライしてみます。これでうまくいくことが多いので、エラーが発生した場合には、時間をおいてみましょう。
次回以降、取得したデータの加工をしていきます。