中古不動産物件をスクレイピングする

いい感じの中古物件を見つけたいです。

スクレイピングして手元でデータ解析をすれば、見つけるヒントが得られれるかもしれんぞ

suumoのサイトを使用します。suumoのサイトは整理されているのでスクレイピングしやすいのがメリットです。

使用するライブラリ

retry: スクレイピング処理をする場合に失敗した場合の再度スクレイピング処理をする際の設定ができます。

下記に各種パラメータの説明があるので、それを元に設定ができます。

https://pypi.org/project/retry/

def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger):
    """Return a retry decorator.

    :param exceptions: an exception or a tuple of exceptions to catch. default: Exception.
    :param tries: the maximum number of attempts. default: -1 (infinite).
    :param delay: initial delay between attempts. default: 0.
    :param max_delay: the maximum value of delay. default: None (no limit).
    :param backoff: multiplier applied to delay between attempts. default: 1 (no backoff).
    :param jitter: extra seconds added to delay between attempts. default: 0.
                   fixed if a number, random if a range tuple (min, max)
    :param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
                   default: retry.logging_logger. if None, logging is disabled.
    """


BeautifulSoup4: HTMLをパースするために使用します。
pandas: 処理後のデータをcsv形式で保存するために使用します。

スクレイピング処理の前準備

東京都の中古物件にアクセスします。

https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999

877ページのデータがあります。

各ページごとにアクセスするために&pn=パラメータを追加します。{}の部分をページ番号を挿入します。

https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&page={}

例えば1ページ目にアクセスするときは下記のうようになります。

https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&pn=1

必要な要素にアクセスできるように要素を確認します。

サイトにアクセスし、右クリックしInspectをクリックします。下記のようなページになります。

図の赤色の部分をクリックするとマウスをホバーするとどの要素が対応しているか確認できます。

物件情報にアクセスするための各種エレメントの要素は下記のようになっています。

“property_unit”と”property_unit property_unit–osusume”が各物件の要素になっています。

Python コードの記述

必要な準備が終わったのでコードを記述していきます。

動作に必要なライブラリをインポートします。

from retry import retry
import requests
from bs4 import BeautifulSoup
import pandas as pd
from urllib.parse import urlparse

URLを設定します。

base_url = "https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&pn={}"

あとでURLから必要な情報を取得するため下記処理を行います。

parsed_url = urlparse(base_url)

URLからデータを取得するための関数です。

retryでデコレートします。

  • tries: 関数の試行回数
  • delay: 再実行時の遅れ時間
  • backoff:リトライするときは2のn乗の間隔でリトライするのが良いと言われます。これはExponetial BackOffと言われる方法で、2に設定している場合は1,2,4,8,16秒…と間隔を開けます。
@retry(tries=3, delay=10, backoff=2)
def get_html(url):
    r = requests.get(url)
    soup = BeautifulSoup(r.content, "html.parser")
    return soup

取得したデータから必要な情報を抽出する関数を設定します。

例えば下記のコードの場合は要素はURL内の要素divからclass名”ui-media-object property_unit-object”を取得し、URLが記述された”a”要素を取得し、その中のhref要素を取得します。

例えば下記のコードの場合は要素はURL内の要素divからclass名”ui-media-object property_unit-object”を取得し、URLが記述された”a”要素を取得し、その中のhref要素を取得します。

base_data["URL"] = "https://suumo.jp" + item.find("div", {"class": "ui-media-object property_unit-object"}).find("a").get("href")

先程と同様にサイトをチェックします。div class名は“ui-media-object property_unit-object”になっています。

“a”要素内のhrefにURLが記述されているのでその要素を取得します。

各物件の情報がdiv classのdottable-lineに記載されているので、そこを確認します。

物件の名称を取得します。dd class名が”dottable-vm”からテキスト要素を取得し改行コードを除きます。

base_data["名称"] = stations[0].find("dd", {"class": "dottable-vm"}).getText().strip()

実際のHTMLの要素は下記のようになります。

物件から名称、販売価格、所在地、沿線、駅、専有面積、築年月、バルコニー、間取りを取得します。

def get_element(items: list, all_data: list) -> list:
    # process each item
    for item in items:
        base_data = {}
        base_data["URL"] = "https://suumo.jp" + item.find("div", {"class": "ui-media-object property_unit-object"}).find("a").get("href")
        stations = item.findAll("div", {"class": "dottable-line"})
        base_data["名称"] = stations[0].find("dd", {"class": "dottable-vm"}).getText().strip()
        base_data["販売価格"] = stations[1].find("span", {"class": "dottable-value"}).getText().strip()
        base_data["所在地"] = stations[2].findAll("dd")[0].getText().strip()
        base_data["沿線, 駅"] = stations[2].findAll("dd")[1].getText().strip()
        base_data["専有面積"] = stations[3].findAll("dd")[0].getText().strip()
        base_data["築年月"] = stations[3].findAll("dd")[1].getText().strip()
        base_data["バルコニー"] = stations[4].findAll("dd")[0].getText().strip()
        base_data["間取り"] = stations[4].findAll("dd")[1].getText().strip()
        all_data.append(base_data)
    return all_data

下記のコードでスクレイピングの下準備をします。データを管理するlistと最大スクレイピングのページ数を指定します。

all_data = []
max_page = 877

最大ページ数まで処理を繰り返します。

  • ページ数ごとにURLを設定します。
  • 指定したURLからHTML要素を取得します。
  • 物件が記述されている各要素を取得します。
  • 先程作成した関数から名称、販売価格、所在地、沿線、駅、専有面積、築年月、バルコニー、間取り要素を取得します。
  • そのデータをCSV形式で保存します。
for page in range(1, max_page+1):
    # define url 
    url = base_url.format(page)

    # get html
    soup = get_html(url)

    # extract all items
    items = soup.findAll("div", {"class": "property_unit property_unit--osusume"})
    print("page", page, "items", len(items))
    all_data = get_element(items, all_data)

    items = soup.findAll("div", {"class": "property_unit"})
    print("page", page, "items", len(items))
    all_data = get_element(items, all_data)


    # convert to dataframe
    df = pd.DataFrame(all_data)
    df.to_csv(parsed_url.netloc + '.real_estate_all.csv')

上手く動作すれば下記のようにcsvファイルで取得できます。

スクレイピングすることで必要な要素を取得するための前準備ができるぞ

これでいい感じの物件を取得したいです。

Close Bitnami banner
Bitnami