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)
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
# 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")
# 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() マッチした文字列を返す。再度文字列を逆順にする
urls = []
urls.append(url) # page 1
# 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)
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 = [] # 駐車場
# 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 = '') # 進捗表示
# 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)
# 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')