Python AI 画像

SUUMOのホームページをWeb Scrapingし、指定の市(区)の中古マンション情報を収集し、CSV出力する。

Step 1で収集するページのURLを設定し、Step 10で出力するCSVファイル名を指定してください
BeautifulSoupを用いてHTMLからデータを抽出します。HTML の取得は requests を使い、HTML のパース処理を Beautiful Soup で実施しています。
解析する場合は、http://www15.plala.or.jp/vffuda/ML_Suumo.html をご利用ください

参考にしたサイトは、こちらです。
【Python】賃貸情報を調べたかったので、BeautifulSoupでスクレイピングしてみた(https://qiita.com/bottusan1073/items/2093b76ff7734d733879)
機械学習を使って東京23区のお買い得賃貸物件を探してみた 〜スクレイピング編〜(http://www.analyze-world.com/entry/2017/10/09/062445)

In [1]:
from bs4 import BeautifulSoup # Web scraping
import urllib3 # url access
import certifi # 証明書
import re # 正規表現
import pandas as pd # Python data analys library
from pandas import Series, DataFrame
import time
In [2]:
# Step 1 get http page for getting last page
# http = urllib3.PoolManager()
http = urllib3.PoolManager(cert_reqs = 'CERT_REQUIRED',
                          ca_certs = certifi.where())

# 東京都(13)
#url = "https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=030&bs=011&ta=13&jspIdFlg=patternShikugun&sc=13101&sc=13102&sc=13103&sc=13104&sc=13105&sc=13113&sc=13106&sc=13109&sc=13110&sc=13111&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&srch_navi=1"
# 京都府(26)京都市
url="https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=060&bs=011&ta=26&jspIdFlg=patternShikugun&sc=26101&sc=26102&sc=26103&sc=26104&sc=26105&sc=26106&sc=26107&sc=26108&sc=26109&sc=26110&sc=26111&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&srch_navi=1"
# 福岡県(40)福岡市
#url = "https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=090&bs=011&ta=40&jspIdFlg=patternShikugun&sc=40131&sc=40132&sc=40133&sc=40134&sc=40135&sc=40136&sc=40137&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&srch_navi=1"
# 沖縄県(47)那覇近郊
#url = "https://suumo.jp/jj/bukken/ichiran/JJ010FJ001/?ar=090&bs=011&ta=47&jspIdFlg=patternShikugun&sc=47201&sc=47205&sc=47208&sc=47209&sc=47211&sc=47212&sc=47213&sc=47320&kb=1&kt=9999999&mb=0&mt=9999999&ekTjCd=&ekTjNm=&tj=0&cnb=0&cn=9999999&srch_navi=1"

# !!! Also change csv file name in step 10

response = http.request('GET', url)
soup = BeautifulSoup(response.data, "html.parser")
In [3]:
# Step 2 retrieve last page number
pages = soup.find_all('ol', class_='pagination-parts')
#print(pages)
pages = str(pages)[::-1] # トータルページが最後のほうにあるので、逆順の文字列に変換
#print(pages)
m = re.search(r'\<\d\d\d\>', pages) # 最初の<ddd>形式(3桁)のトータルページ数を探す
if m is None:
    m = re.search(r'\<\d\d\>', pages) # 最初の<dd>形式(二桁)のトータルページ数を探す
    if m is None:
            m = re.search(r'\<\d\>', pages) # 最初の<d>形式(一桁)のトータルページ数を探す
#print(m.group(0)[::-1])
last_page_number = int(m.group(0).replace("<", "").replace(">", "")[::-1]) # group()	マッチした文字列を返す。再度文字列を逆順にする
In [4]:
urls = []
urls.append(url) # page 1
In [5]:
# Step 3 set url name with pages (2 to last page)
for i in range(last_page_number - 1): # page 2 to last page
    page_num = str(i + 2)
    url_page = url + '&pn=' + page_num
    urls.append(url_page)
In [6]:
names = [] # マンション名
prices = [] # 価格(万円)
addresses = [] # 住所
routes = [] # 路線
stations = [] # 最寄り駅
walks = [] # 徒歩(駅徒歩~分)
areas = [] # 専有面積(m2)
layouts = [] # 間取り
balconys = [] # バルコニー(m2)
ages = [] # 築年月
detail_urls = [] # 詳細URL

# 物件ごとの情報
admins = [] # 管理費
repairs = [] # 修繕積立費
floors = [] # 部屋のある階 メゾネットタイプは一律0、地下はマイナス
directions = [] # 方角
reforms = [] # リフォーム
units = [] # 総戸数
heights = [] # 建物高(階)
rights = [] # 権利形態
usage = [] # 用途地域
parkings = [] # 駐車場
In [7]:
# Step 4 get each url page

print("Total pages:", last_page_number)
page = 0
for url in urls:
    response = http.request('GET', url)
    soup = BeautifulSoup(response.data, "html.parser")
    
# Step 5 for a condo
    summary = soup.find("div", {'id':'js-bukkenList'})
    condos = summary.find_all("div", {'class':'property_unit'})
    # print(len(condos)) # 表示建物数/ページ デフォルト30
    condo = summary.find("div", {'class':'property_unit-content'}) # 表示建物数 デフォルト30   

    for condo in condos:
        name = condo.find('dd', {'class':'dottable-vm'}).string
        _price = condo.find('span', {'class':'dottable-value'}).string
        price = _price.rstrip("万円")
        price = re.sub('億', "", price) # 物件価格に億が含まれている場合
        if len(price) <= 2:
            price = price + str("0000")
        address = condo.find_all('dd')[2].string
        text = condo.find_all('dd')[3].string
        route = text.split("「")[0]
        if "「「" in text: # 沿線・駅に「「xx」バス停」の表記の場合
            _station = text.split("「「")[1]
            station = _station.split("」")[0]
            _walk = _station.split("バス停」")[1].split("歩")[1]
            walk = _walk.rstrip("分")
        else:
            _station = text.split("「")[1]
            station = _station.split("」")[0]
            if "km" in text: # 駅から車xxkmの表記の場合
                walk = "99"
            else:
                _walk = _station.split("」")[1].split("歩")[1]
                walk = _walk.rstrip("分")
        area = condo.find_all('dd')[4].contents[0].split('m')[0]
        layout = condo.find_all('dd')[5].string
        if layout == "ワンルーム":
            layout = "1room"
        balcony = condo.find_all('dd')[6].contents[0].split('m')[0]
        if balcony == "-":
            balcony = "0.0"
        age = condo.find_all('dd')[7].string
        
        # 物件ごとの詳細情報へのリンク
        h2 = condo.find('h2', {'class':'property_unit-title'})
        a = h2.find('a')
        href = a.get('href')
        href = href.strip(href.split('/')[-1]) # 最後の/以下を削除
        link = 'https://suumo.jp' + href + 'bukkengaiyo/'
        detail_url = link
        
# Step 6 物件ごとに子ページスクレーピング、ただし遅くなるので実施しないほうが良い
        # response_child = http.request('GET', link)
        # soup_child = BeautifulSoup(response_child.data, "html.parser")
        # summary_child = soup_child.find_all('tbody', {'class':'vat tal'})
        # summary_child[0].find_all('td')[2] 戸数
        # summary_child[0].find_all('td')[3] 最多価格帯
        # summary_child[0].find_all('td')[4] 価格
        # summary_child[0].find_all('td')[5] 管理費
        # summary_child[0].find_all('td')[6] 修繕積立費
        # summary_child[0].find_all('td')[9] 間取り
        # summary_child[0].find_all('td')[14] 部屋のある階・階建て
        # summary_child[0].find_all('td')[15] 向き
        # summary_child[0].find_all('td')[16] リフォーム
        # summary_child[1].find_all('td')[2] 総戸数
        # summary_child[1].find_all('td')[3] 構造・階建て
        # summary_child[1].find_all('td')[5] 権利形態
        # summary_child[1].find_all('td')[6] 用途地域
        # summary_child[1].find_all('td')[7] 駐車場
    
# Step 7 物件データ登録
        names.append(name)
        prices.append(price)
        addresses.append(address)
        routes.append(route)
        stations.append(station)
        walks.append(walk)
        areas.append(area)
        layouts.append(layout)
        balconys.append(balcony)
        ages.append(age)
        detail_urls.append(detail_url)
    
    time.sleep(1) # 連続でホームページにアクセスしないようにする 私の計算機だと処理が遅いので、1秒程度に設定

    page += 1
    print(page, ' ', end = '') # 進捗表示
Total pages: 46
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  
In [8]:
# Step 8
#各リストをシリーズ化
names = Series(names)
prices = Series(prices)
addresses = Series(addresses)
routes = Series(routes)
stations = Series(stations)
walks = Series(walks)
areas = Series(areas)
layouts = Series(layouts)
balconys = Series(balconys)
ages = Series(ages)
detail_urls = Series(detail_urls)

suumo_df = pd.concat([names, prices, addresses, routes, stations, walks, areas, layouts, balconys, ages, detail_urls], axis=1)

suumo_df.columns=['マンション名','価格(万円)','住所','路線','駅','徒歩(分)','専有面積(m2)','間取り','バルコニー(m2)','築年月','詳細URL']
suumo_df.tail(1)
Out[8]:
マンション名 価格(万円) 住所 路線 徒歩(分) 専有面積(m2) 間取り バルコニー(m2) 築年 詳細URL
1354 リーガル京都御所東 20000 京都府京都市上京区梶井町御車道通清和院口上る 叡山電鉄叡山本線 出町柳 6 189.09 3LDK+S(納戸) 44.37 2008年3月 https://suumo.jp/ms/chuko/kyoto/sc_kyotoshikam...
In [9]:
# Step 9 CSVファイル出力 (separator: Tab)
#suumo_df.to_csv('suumo_chuko_tokyo.csv', sep = '\t', encoding = 'utf-16')
suumo_df.to_csv('suumo_chuko_kyoto.csv', sep = '\t', encoding = 'utf-16')
#suumo_df.to_csv('suumo_chuko_fukuoka.csv', sep = '\t', encoding = 'utf-16')
#suumo_df.to_csv('suumo_chuko_naha.csv', sep = '\t', encoding = 'utf-16')
In [ ]: