初学者が勉強するBeautiful Soup. youtube動画に則り②

## はじめに

こちらは以前記入した[こちら](https://qiita.com/The_Boys/private/da2eb6895ca400730fb1)の続きの内容となります。

簡単にあらすじを紹介させて頂きます。
前回はBeatutiful Soupを用いて１つの塊の情報(観光地1)を取得しました。利用したサイトは[こちら](https://scraping-for-beginner.herokuapp.com/ranking/)です。今回１つではなく、10個全てを取得したいと思います。

![2022-07-02_01h25_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/24b6c980-9353-88a3-a505-424fa660e06f.png)


### バックナンバー
- [初学者が勉強するSelenium。①](https://qiita.com/The_Boys/items/6d53992ff9323c3c2597)
- [初学者が勉強するSelenium。②](https://qiita.com/The_Boys/items/7b0fa0a75748f65b1edf)
- [seleniumはCopy_full_xpathで少し楽になる](https://qiita.com/The_Boys/items/4c0fa1bcac066de2c643)
- [初学者が勉強するBeautiful Soup①](https://qiita.com/The_Boys/items/da2eb6895ca400730fb1)
- [初学者が勉強するBeautiful Soup②](https://qiita.com/The_Boys/items/43d8837fce179cca237f)
- [初学者が勉強するBeautiful Soup②](https://qiita.com/The_Boys/items/43d8837fce179cca237f)
- [Beautiful Soupで画像を取得](https://qiita.com/The_Boys/items/6d7dffc982760e8d8066)
- [BeautifulSoupでURLを一括取得](https://qiita.com/The_Boys/items/14d42da70d26fad6dde2)
- [複数ページにわたる情報の取得。スクレイピング。](https://qiita.com/The_Boys/items/0862cb8a72100519aee9)
- [複数に渡るページでの画像収集スクレイピング](https://qiita.com/The_Boys/items/9486026c20973bc74787)



-  教材

https://www.youtube.com/watch?v=VRFfAeW30qE&t=1402s
- 開発環境

Windows11 / jupyter lab

<details><summary>前回のコード</summary><div>
こちらも詳細は前頁をご参照ください。

```.tsx
# ライブラリやモジュールをimport
from bs4 import BeautifulSoup
import requests
import bs4

url = "https://scraping-for-beginner.herokuapp.com/ranking/"
res = requests.get(url)

soup = BeautifulSoup(res.text, "html.parser") # "html.parser"⇒htmlの構造を解析するもの。

soup      = BeautifulSoup(res.text, "html.parser")
spots     = soup.find_all("div",attrs = {"class":"u_areaListRankingBox"})    #特定の部分を抜き出し。
spot      = spots[0]                                                         #更にその１番目を抜き出し。 
spot_name = spot.find_all("div",attrs = {"class":"u_title col s12"})

spot_name[0].find("span",attrs = {"class": "badge"}).extract()               #邪魔な要素を排除。
spot_name[0] = spot_name[0].text.replace("\n","")                            #ここでようやく観光地１をget

#評価点
eval_num = spot.find("div",attrs = {"class":"u_rankBox col s12"}).text
eval_num = float(eval_num.replace("\n",""))

categoryItems = spot.find_all("div",attrs = {"class":"u_categoryTipsItem col s12"})
# categoryItems = categoryItems[0].find_all("dl") 
categoryItems = categoryItems[0].find_all("dl")
```


```.tsx:
#for文。更に観光地名と評点を追加。

details = {}

for categoryItem in categoryItems:        #この行の意味が分からない。
    category = categoryItem.dt.text       #カテゴリにはそれぞれの評価項目が入る。#感想は入らない。
    rank = float(categoryItem.span.text)  #その評価項目の評価点が入る。
    
    details[category] =rank
    

details["観光地名"] = spot_name
details["評価点"]   = eval_num

details= details[['観光地名','評価点','楽しさ','景色','アクセス','人混みの多さ']]#カラム並び替え。
details
```
</div></details>

## 実装


<details><summary>①段階｡前回のおさらいと相違点。</summary><div>

ここは前回といつも通り。htmlの中から､欲しい情報をfind_allする所まで進めます。
```.tsx:
# ライブラリやモジュールをimport
from bs4 import BeautifulSoup
import requests
import bs4

url = "https://scraping-for-beginner.herokuapp.com/ranking/"
res = requests.get(url)

soup = BeautifulSoup(res.text, "html.parser") # "html.parser"⇒htmlの構造を解析するもの。

spots     = soup.find_all("div",attrs = {"class":"u_areaListRankingBox"}) #その全体の中から欲しい情報があるエリアを絞る。
spots
```

これも前回と同じ通り。前回の手順③をご参照くださいませ。このように元となる要素を取得してください。
```.tsx:
#spots       #全体
spots[0]     #1番目
#spots[1]    #2番目
```
前回は`spot[0]`のみでしたが、今回は`spot全体`から要素を取ってくることです。
それを踏まえて②へ進みましょう

</div></details>


<details><summary>②段階｡ひとまず全体から俯瞰｡</summary><div>

これは結論のコードを先に提示した方がイメージがわくかもしれません。

```.tsx
data = []

#観光地名
spots     = soup.find_all("div",attrs = {"class":"u_areaListRankingBox"})    #特定の部分を抜き出し。

for spot      in spots: 
    spot_name = spot.find_all("div",attrs = {"class":"u_title col s12"})

    spot_name[0].find("span",attrs = {"class": "badge"}).extract()               #邪魔な要素を排除。
    spot_name[0] = spot_name[0].text.replace("\n","")                            #ここでようやく観光地１をget

    #評価点
    eval_num = spot.find("div",attrs = {"class":"u_rankBox col s12"}).text
    eval_num = float(eval_num.replace("\n",""))

    categoryItems = spot.find_all("div",attrs = {"class":"u_categoryTipsItem col s12"})
    # categoryItems = categoryItems[0].find_all("dl") 
    categoryItems = categoryItems[0].find_all("dl") 
    details = {}

    for categoryItem in categoryItems:
        category = categoryItem.dt.text
        rank = float(categoryItem.span.text)

        details[category] =rank

    
    details["観光地名"] = spot_name
    details["評価点"]   = eval_num
    data.append(details)


```

…見覚えがあるコードですよね？｢前回とほとんど変わらないじゃん…と｣。
更に分かりやすく理解して頂く為、両者を比較を提示致します。是非画像を拡大してご覧いただければ。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/3baf9315-9a9a-612e-93d5-7a29f66040b7.png)


それでは間違い探しです。違いはどこでしょうか？
答えは2~3個程度です。`data = []`があったり`for文と段落`、`コードブロックの分け方`位でしょうか。なんだか出来そうな気がしませんか:fist:？

![2022-07-02_22h20_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/8c40283b-1f08-d408-86a9-d8237302877d.png)

</div></details>


<details><summary>③相違点の詳細確認</summary><div>
それでは実際に相違点の確認をしてみましょう。


```.tsx
#複数取得ver
data = []

#観光地名
for spot in spots: 
```


```.tsx:
#単数取得ver
spot = spots[0] 
```

ご覧の通り､複数取得は`for文`ですね。dataというインスタンス(箱)を作成して1周したら、その内容を箱に詰めてもう1周という事ですね。

- 単数取得を完成させる。⇒それを繰り返す。
このようなイメージをして頂ければ。

他にも後半も段落が異なります。

```.tsx:
#単数取得
details = {}

for categoryItem in categoryItems:        #要素を一つづつcategoryItemに入れる。
    category = categoryItem.dt.text       #カテゴリにはそれぞれの名前(dt要素)が形式で入る。
    rank = float(categoryItem.span.text)  #その評価項目の評価点(span要素)がテキスト形式で入る。
    
    details[category] =rank               #辞書に格納
    

details["観光地名"] = spot_name           #辞書に追加。
details["評価点"]   = eval_num            #辞書に追加。
details
```


```.tsx:
#複数取得
 for categoryItem in categoryItems:
        category = categoryItem.dt.text
        rank = float(categoryItem.span.text)

        details[category] =rank

    
    details["観光地名"] = spot_name
    details["評価点"]   = eval_num
    data.append(details)
```

`複数取得の場合は観光地名と評価点もfor文の中に格納されています。`

それらを踏まえた上で`for文の中にfor文`がある内包表記の流れを確認してみます。
汚い画像ですがご了承ください

![2022-07-02_22h42_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/fcf916f1-9e49-9668-00c7-e66f69a19d9a.png)

…いかがですかね？自分なりに分かりやすく説明したつもりなのですが、
かなり見栄えは悪いですね。
人に教えるスキルはまだ持ち合わせていなくて…。


</div></details>


<details><summary>④段階</summary><div>
それでは結果を確認してみましょう。

こちらのコードは前回の⑥又は総まとめをご参照下さい。
要はdf化して出力しています。
```.tsx
import pandas as pd
df = pd.DataFrame(data)

df = df[['観光地名','評価点','楽しさ','景色','アクセス','人混みの多さ']]#カラム並び替え。
df
```

![2022-07-02_23h02_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/59b59ab3-b50c-2265-1047-89d8c7e96621.png)




</div></details>

総まとめコード


```.tsx:
# ライブラリやモジュールをimport
from bs4 import BeautifulSoup
import requests
import bs4
import pandas as pd

url = "https://scraping-for-beginner.herokuapp.com/ranking/"
res = requests.get(url)

soup = BeautifulSoup(res.text, "html.parser") # "html.parser"⇒htmlの構造を解析するもの。

data = []

#観光地名
spots     = soup.find_all("div",attrs = {"class":"u_areaListRankingBox"})    

for spot      in spots: 
    spot_name = spot.find_all("div",attrs = {"class":"u_title col s12"})

    spot_name[0].find("span",attrs = {"class": "badge"}).extract()               
    spot_name[0] = spot_name[0].text.replace("\n","")                            

    #評価点
    eval_num = spot.find("div",attrs = {"class":"u_rankBox col s12"}).text
    eval_num = float(eval_num.replace("\n",""))

    categoryItems = spot.find_all("div",attrs = {"class":"u_categoryTipsItem col s12"})
    categoryItems = categoryItems[0].find_all("dl") 
    details = {}

    for categoryItem in categoryItems:
        category = categoryItem.dt.text
        rank = float(categoryItem.span.text)

        details[category] =rank

    
    details["観光地名"] = spot_name
    details["評価点"]   = eval_num
    data.append(details)
    

df = pd.DataFrame(data)

df = df[['観光地名','評価点','楽しさ','景色','アクセス','人混みの多さ']]#カラム並び替え。
df
```


## 終わりに。

所処説明が冗長になってしまい申し訳ございません。
自分の説明スキルの無さを自覚する事もありましたが､
まだ初めて間もないのでとりあえず数をこなしたいと思います。

ポイントとして
- 苦労して単数取得できればfor文を回してまとめてとる事が出来る。
- その為には構造を見抜くhtml読解力とfor文に親しむ事。(内包表記も)

課題としては
- for文や長いコードの際には人に分かりやすく説明文字起こしができていない実感がある。
- 果たしてこのfor文を応用して別のサイトからスクレイピングをする事が出来るのか？練習を重ねる事。
- スクレイピングとfor文は相性が良い。他の内包表記やlambda式も用いてスクレイピングができるようになること。

次回は画像をまとめて取得と思います。勿論[こちらのサイト](https://scraping-for-beginner.herokuapp.com/ranking/)で。