SUUMOの賃貸物件検索からレオパレス物件を除外する拡張機能(userscript)を作る

初投稿です．

# なにこれ

SUUMOで賃貸アパートを探していると，レオパレス物件ばかり出てきて嫌になりませんか？

私もSUUMOで検索するとレオパレス物件ばかり(しかも何部屋も募集があってスクロールが大変)だし、ネットには

> インターネットが最悪！壁が薄すぎて隣人のいびきすら聞こえる！雨漏りする！天井が剥がれた！マナーが最悪！家具家電付きで相殺されないほどのマイナス！なんだかんだ全部足したら月費用普通に高い！

などのレビューがあふれています．

しかしSUUMOにNGワード機能はないらしいので，Greasy Forkでuserscript×2を作りました．

# 機能

+ SUUMOの賃貸物件検索から物件名に **"レオパレス"が含まれる物件** を除外 ⇒ [SUUMOからレオパレス物件を除外(Exclude Leopalace from SUUMO)](https://greasyfork.org/ja/scripts/434556-suumo%E3%81%8B%E3%82%89%E3%83%AC%E3%82%AA%E3%83%91%E3%83%AC%E3%82%B9%E7%89%A9%E4%BB%B6%E3%82%92%E9%99%A4%E5%A4%96-exclude-leopalace-from-suumo)
+ SUUMOの賃貸物件検索に **除外ワードを設定** し，物件名にそのワードが含まれる物件を除外 ⇒ [SUUMOの賃貸物件検索に除外ワードを設定(SUUMO Chintai NG)](https://greasyfork.org/ja/scripts/434625-suumo%E3%81%AE%E8%B3%83%E8%B2%B8%E7%89%A9%E4%BB%B6%E6%A4%9C%E7%B4%A2%E3%81%AB%E9%99%A4%E5%A4%96%E3%83%AF%E3%83%BC%E3%83%89%E3%82%92%E8%A8%AD%E5%AE%9A-suumo-chintai-ng)

ができます．
  
# 使用方法

## 1.Tampermonkeyのインストール

まず使用ブラウザの拡張機能インストールページから _Tampermonkey_ をインストールしてください．
Chromeなら[Chrome ウェブストア](https://chrome.google.com/webstore/category/extensions?hl=ja)，Firefoxなら[Firefoxアドオン](https://addons.mozilla.org/ja/firefox/)からインストールできると思います．

## 2.Greasy Forkにアクセス

Greasy Fork(https://greasyfork.org/ja) にアクセスし「SUUMO」と検索すると私が作ったuserscriptが2つ出てくると思います．上のリンクから直接飛んだ場合はここは飛ばしていいです．
<img width="450" alt="greasyfork.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550225/44329eeb-8c40-e186-ef3f-54b3825e8431.png">

選択してみるとこういうページが出ると思うので「スクリプトをインストール」を押すとTampermonkeyが開くので「インストール」というボタンを押して完了です．
※userscriptのインストールには十分注意してください※
<img width="450" alt="extensionpage.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550225/208b685f-b9bc-f4e3-31a6-40364cb72bd7.png">
<img width="450" alt="installpage.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550225/cb6e3a80-0cd1-860c-3d1e-18263c7810ee.png">

## 3.SUUMOのページで使ってみる

今回は除外ワードの方のuserscriptをインストールしてみました．
それではSUUMO東海版で
> 静岡県／浜松市中区 3.5万円以下 ワンルーム／1K アパート 駐車場あり／バス・トイレ別

を条件として検索してみましょう．~~浜松市にした理由はレオパレスがいっぱいあったからです．~~一人暮らしの学生で車ありみたいな想定です．

そのデモ動画です．除外前⇒"レオパレス"非表示⇒"浜松"非表示⇒リセットの流れです．

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">SUUMOの賃貸物件検索に除外ワード設定する拡張機能と，レオパレスを名前に含む物件を非表示にする拡張機能を作りました<br>除外ワード: <a href="https://t.co/o17UtCgCzK">https://t.co/o17UtCgCzK</a><br>レオパレス除外: <a href="https://t.co/82SupsUFCI">https://t.co/82SupsUFCI</a><br><br>↓除外ワード設定の動画です <a href="https://t.co/D2WgFTviGK">pic.twitter.com/D2WgFTviGK</a></p>&mdash; まぁじ (@Hals_SC) <a href="https://twitter.com/Hals_SC/status/1454020439278174211?ref_src=twsrc%5Etfw">October 29, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

# 使用時の注意

### なんかuserscriptが効かない...

賃貸物件検索してもuserscriptが効かない場合には「部屋ごとに表示」などを経由することで拡張機能が効くURLになると思います．

### 画面が変なのになった

除外を繰り返すと「まとめてお問い合わせする」ボタンが大量に出てきますが，~~直せません~~そういう仕様です．面白がってやってください．
リロードすれば全部リセットされます．

### なにかあったら

[GitHub](https://github.com/iharuki79/exclude-leopalace)まで連絡お願いします．

# 実装について

ここからは特に見なくてもいいゾーンです．

> **userscriptとは？**
> ユーザースクリプトは、Web ページを見たり使ったりするときの主導権をあなたに与えます。スクリプトをインストールすれば、ページの機能を使いやすくしたり、新たな機能を追加したり、迷惑なものを取り除いたりして、訪れたサイトをより便利にカスタマイズすることができます。Greasy Fork では、ユーザーが開発・投稿したスクリプトを公開しています。これらのスクリプトは自由にインストールでき、使うのも簡単です。 - https://greasyfork.org/ja より引用

まぁつまりはWebページをこっち側からいじれる機能です．[YouTubeの配信チャットを某ニコニコのように流れるようにした](https://greasyfork.org/ja/scripts/411442-flow-youtube-chat)り[AtCoderのパフォーマンス予測をした](https://greasyfork.org/ja/scripts/369954-ac-predictor)りなど様々なuserscriptが投稿されています．

今回私が書いたuserscript自体はJavaScript+jQueryで書かれています．
物件名が`.cassetteitem_content-title`というクラスにあるため，このテキストを取得して`includes`関数で"レオパレス"が含まれているか調べています．その物件情報全体が`.cassetteitem`クラスに指定されているため，最も近い祖先要素を返す`closest`関数を使って物件タブを呼び出し`hidden=true`に指定して非表示としています．

```javascript:レオパレス非表示にする方.js
(function (){
    const titles=$(".cassetteitem_content-title");
    const exName=$("レオパレス").val();
    let cnt=0;
    for(let i=0;i<titles.length;i++){
        try{
			let item=titles[i].closest(".cassetteitem");
            if(titles[i].textContent.includes(exName) && !item.hidden){
                item.hidden=true;
                cnt++;
            }
        }catch(TypeError){
            {}
        }
    }
    console.log(cnt+"件の"+exName+"物件を除外");
})();
```

除外ワード設定する方は上のコードを`onClick`パラメータにしたボタンを追加しててごにょごびょしてるだけです(雑)．jQuery初めて書いたので何にもわかりませんでした．

```javascript:除外ワード設定する方.js
(function (){
    $(".pagecaption").append(`<br>Exclude:<input value='レオパレス' id='excludeName' style="width:100pt">`);
    $(".pagecaption").append(`<input type='submit' value='除外' onClick='(function (){
        const titles=$(".cassetteitem_content-title");
        const exName=$("#excludeName").val();
        let cnt=0;
        for(let i=0;i<titles.length;i++){
            try{
				let item=titles[i].closest(".cassetteitem");
                if(titles[i].textContent.includes(exName) && !item.hidden){
                    item.hidden=true;
                    cnt++;
                }
            }catch(TypeError){
                {}
            }
        }
        console.log(cnt+"件の"+exName+"物件を除外");})();'
    >`);
    $(".pagecaption").append(`<input type='submit' value='リセット' onClick='(function (){
            const titles=$(".cassetteitem_content-title");
            for(let i=0;i<titles.length;i++){
                try{
                    titles[i].closest(".cassetteitem").hidden=false;
                }catch(TypeError){
                    {}
                }
            }
			console.log("表示をリセット");
		})();'
    >`);
})();
```

# おわりに

私のように賃貸アパートを探して不要な物件がヒットすることにイライラする人が一人でも減ることを望んでいます．アパートは無事決まり大家さんからの承認も来ました．やったね！  
ちなみにSUUMOのサイトをスクレイピングする場合User-agentを指定してブロックしているので，ヘッダーに別のブラウザを指定すれば取得できます．でも規約違反かもしれません．  
スクレイピングするときにはそのサイトの利用規約をしっかりと読み，違反行為じゃないことを確かめてからやりましょう．このuserscriptはデータを使用してないし非表示にしてるだけなので多分大丈夫だと思います．怒られたら消します．

## 参考

+ [【SUUMO】不動産売買・住宅購入・賃貸情報ならリクルートの不動産ポータルサイト](https://suumo.jp/)
+ [Greasy Fork - 便利で安全なユーザースクリプトを](https://greasyfork.org/ja) 
+ [SUUMOのアプリでレオパレスを除外する方法あれば教えて下さい - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10243275765)
+ [Element.closest() - Web API | MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/closest)
+ [String.prototype.includes() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/includes)