suumo賃貸物件検索サイトのスクレイピング

# はじめに
pythonのBeautifulSoupを使用して、押上駅周辺のsuumoの賃貸物件情報をスクレイピングしました。以下の内容はコードとその解説となります。
また、スクレイピングする際はそのサイトの利用規約に従う必要があります。
suumoについては個人利用であれば問題はないとのことでした。
(suumoの利用規約はこちら[suumo利用規約](https://cdn.p.recruit.co.jp/terms/suu-t-1003/index.html))
その他、スクレイピングを行う際はいろいろと気をつけなければならないことがあるので注意してください。

# 目次
・コード
・解説
 　1. 必要なモジュールのインポート
 　2. リストの作成
 　3. ユーザーエージェントの設定
 　4. スクレイピング
 　5. csvファイルで出力
・まとめ  

# コード
今回、私が作成したコードは以下になります。（jupyternotebookを使用しています）

```Python
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm as tqdm 
import pandas as pd
import time

# スクレイピングしたデータを一時的に格納するリスト
name = []
station = []
price = []
sikikinreikin = []
room = []
age = []
address = []

# ユーザーエージェント名を設定
user_agent = "(ユーザーエージェント名)"
header = {'user-agent':user_agent}

#　スクレイピング開始
for p in tqdm(range(1,539)):
        p=str(p)
        url="https://suumo.jp/jj/chintai/ichiran/FR301FC005/?shkr1=03&cb=0.0&shkr3=03&rn=0440&shkr2=03&mt=9999999&ar=030&bs=040&shkr4=03&ct=9999999&ra=013&ek=044006820&cn=9999999&mb=0&fw2=&et=9999999" + "&page=" + p
        
        urls=requests.get(url,headers=header)
        time.sleep(2)
        urls.encoding = urls.apparent_encoding 
        soup=BeautifulSoup()
        soup=BeautifulSoup(urls.content,"html.parser")
        house_name = soup.find_all("a",class_="js-cassetLinkHref")
        station_name = soup.find_all("div",style="font-weight:bold")
        table_data = pd.read_html(urls.content)
        for h in house_name:
            name.append(h.text)
        for s in station_name:
            station.append(s.text)
        
        for i in range(0,30):
            table = table_data[i]
            price.append(table[0])
            sikikinreikin.append(table[1])
            room.append(table[2])
            age.append(table[3])
            address.append(table[4])

# リストをシリーズに変換
name_s=pd.Series(name)
station_s=pd.Series(station)
price_s=pd.Series(price)
sikikinreikin_s=pd.Series(sikikinreikin)
room_s=pd.Series(room)
age_s = pd.Series(age)
address_s=pd.Series(address)

# シリーズを結合
df=pd.concat([name_s,station_s,price_s,sikikinreikin_s,room_s,age_s,address_s],axis=1)

# カラム名を付け直す
df.columns=["name","station","price","sikikinreikin","room","age","address"]

# csvで保存
df.to_csv('data/suumo.csv',header=True, index=False)
```

# 解説
上から解説していきます。
## 1. 必要なモジュールのインポート

```Python
import requests
from bs4 import BeautifulSoup
from tqdm import tqdm as tqdm 
import pandas as pd
import time
```
requestsはwebページを取得際に必要となります。
BeautifulSoupはHTMLを解析する際に必要となります。
tqdmはスクレイピングの進行状況を確認する際に使用します。
pandasは取得したデータをデータフレームにする際に使用します。
timeは連続してwebページにアクセスしないようにするため必要です。

## 2. リストの作成
```Python
name = []
station = []
price = []
sikikinreikin = []
room = []
age = []
address = []
```
スクレイピングして取得したデータを一時的に保存しておくリストです。

## 3. ユーザーエージェントの設定
```Python
user_agent = "(ユーザーエージェント名)"
header = {'user-agent':user_agent}
```
スクレイピングするにあたってユーザーエージェントを設定する必要があります。
ユーザーエージェントを設定しないと不自然なアクセスとみなされ、通信が切断される可能性があります。
使用しているブラウザで[このサイト](https://testpage.jp/tool/ip_user_agent.php)にアクセスするとユーザーエージェントを確認できます。ページの中にブラウザのユーザーエージェントという項目があるので、その値をuser_agentに代入してください。

(この辺りはもう少し調べて学ぶ必要があると感じました。)


## 4. スクレイピング

まずはスクレイピングしたいサイトについて確認します。
押上駅周辺で検索したところ物件数は16136件でした。(2022年1月現在)
また、物件を部屋ごとに表示させたところ、538ページあることがわかりました。
つまり、538ページ分のwebページにアクセスする必要があることになります。
![スクリーンショット 2022-01-28 16.19.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517778/0ed31026-c8ef-986f-03d8-dc10748c7383.png)
そこで、下記のようなコードで５３８ページ分のページにアクセスしました。

```Python
for p in tqdm(range(1,539)):
        p=str(p)
        url="https://suumo.jp/jj/chintai/ichiran/FR301FC005/?shkr1=03&cb=0.0&shkr3=03&rn=0440&shkr2=03&mt=9999999&ar=030&bs=040&shkr4=03&ct=9999999&ra=013&ek=044006820&cn=9999999&mb=0&fw2=&et=9999999" + "&page=" + p
        
        urls=requests.get(url,headers=header)
        time.sleep(2)
```

urlの後ろに&page=pと指定するとそのページのpページ目にアクセスすることができます。
url + &page= + p でrangeを指定しfor文を回すことで範囲内のページ全てにアクセスすることができます。

requests.get(url,headers=header)で指定のurlのwebページを取得します。この時、引数にheaders=header (headerは先ほど設定したユーザーエージェント名)とすることでヘッダーを設定できます。

ここで一度webサイトにアクセスしたので、連続してwebサイトにアクセスするのを防ぐためにtime.sleep(2)で2秒待ちます。

```Python
        urls.encoding = urls.apparent_encoding 
        soup=BeautifulSoup()
        soup=BeautifulSoup(urls.content,"html.parser")
        house_name = soup.find_all("a",class_="js-cassetLinkHref")
        station_name = soup.find_all("div",style="font-weight:bold")
        table_data = pd.read_html(urls.content)
```
urls.apparent_encodingは文字化け対策で必要です。

BeautifulSoup(urls.content,"html.parser")で取得したwebページのhtmlを解析します。

まずは家の名前を取得するのですが、その前にhtmlのどのタグに家の名前の要素があるかどうかを確認する必要があります。
要素の確認は、スクレイピングしたいwebページにアクセスし、取得したい要素の上で右クリック→検証で行うことができます。

![スクリーンショット 2022-01-31 11.55.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517778/ee2a7179-c547-69bd-106b-91aeb173bcad.png)


確認したところ、物件の名前は"a"タグのclass="js-cassetLinkHref"にあることがわかりました。
soup.find_all("a",class_="js-cassetLinkHref")と書くことで、そのページ内の全ての"js-cassetLinkHref"クラスの要素を取得します。
classはpythonの予約語として設定されているので、コードを書くときはclass_のようにします。
最寄駅については、今回は太字になっているところのみを取得しました。

また、pd.read_html(urls.content)とすることで、そのwebページの中のtable属性のデータをリスト型で取得できます。今回はこれを利用し、敷金礼金や部屋の広さといったデータを取得しました。
今回のwebページでは0~29番目までのテーブルが物件のデータになっているのでfor文でリストに格納していきます。

```Python
        for h in house_name:
            name.append(h.text)
        for s in station_name:
            station.append(s.text)
        
        for i in range(0,30):
            table = table_data[i]
            price.append(table[0])
            sikikinreikin.append(table[1])
            room.append(table[2])
            age.append(table[3])
            address.append(table[4])
```

## 5. csvファイルで出力
```Python
# リストをシリーズに変換
name_s=pd.Series(name)
station_s=pd.Series(station)
price_s=pd.Series(price)
sikikinreikin_s=pd.Series(sikikinreikin)
room_s=pd.Series(room)
age_s = pd.Series(age)
address_s=pd.Series(address)

# シリーズを結合
df=pd.concat([name_s,station_s,price_s,sikikinreikin_s,room_s,age_s,address_s],axis=1)

# カラム名を付け直す
df.columns=["name","station","price","sikikinreikin","room","age","address"]

# csvで保存
df.to_csv('data/suumo.csv',header=True, index=False)
```

最後に、作成したリストをシリーズに変換&結合し、カラム名を付け直したらcsvファイルで保存します。

# まとめ
上記のコードで、スクレイピングにかかった時間は約30分でした。
また、今回取得したデータはそのままでは汚すぎて使うことができません。そのため、前処理をして必要なデータを使える形にする必要があります。
リストに格納する際のコードを工夫すれば前処理にかかる手間が少し軽くなるかもしれません。