Python を活用しながら Web サイトのデータ収集を効率化（２）【実装編】

# はじめに
みなさん、こんにちは。
株式会社キカガクの機械学習講師 藏野です。
キカガクは、「AI を含めた先端技術の研修」を行っている会社です。

この記事は、「Web スクレイピングで特定のデータを取得して CSV で出力」までの実務的な内容を取り扱います。

スクレイピングの記事は多くありますが、**活用を見据えてデータを取得し保存する**ところまで取り組みたいという方は、ぜひ参考にされてください。

この記事は、[Python を活用しながら Web サイトのデータ収集を効率化（１）【環境構築編】](https://qiita.com/nino_1993/items/eda5cb7f688f0c8be60a)の続きとなっています。

必ず読む必要はありませんが、スクレイピングの仕組みを復習したい方は一読をオススメします。

## 目次

1. スクレイピングの手法
2. 環境構築
3. 実践
4. おわりに
5. お知らせ

## Web スクレイピングの手法
スクレイピングには、以下の 2 つの手法があります。

- Selenium を使用して、ブラウザを自動操作することでデータを取得する手法
- Beautiful Soup という Python パッケージを使用して、データを取得する手法

各手法の特徴として、Selenium は**データ取得とログイン処理などのブラウザ自動操作**を行うことができ、 Beautiful Soup は **HTML を解析してデータ取得**を行うことができます。

スクレイピングを行うには両方の手法を扱えること理想ですが、今回は **Selenium** を使用してスクレイピングを行います。

## 環境構築
環境構築は、はじめに共有した[こちら](https://qiita.com/nino_1993/items/eda5cb7f688f0c8be60a)と同じなので、細かい説明は割愛します。

- ブラウザ：Google Chrome
- 実行環境：Google Colaboratory（以下 Colab ）

### パッケージのインストール
Colab でノートブックを作成後に、Selenium と WebDriver（ ChromeDriver ）をインストールしてインポートまで行います。

```bash:SeleniumとWebDriverのインストール
!apt-get update
!apt install chromium-chromedriver
!cp /usr/lib/chromium-browser/chromedriver/usr/bin
!pip install selenium
```
```python:webdriverとopitonsのインポート
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
```
```python:Headlessモードに設定
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
browser = webdriver.Chrome('chromedriver',options=options)
```
#### Headless モード
Selenium は、ブラウザの自動操作を行うことができるので、自動操作用のブラウザを立ち上げて様々な処理を行います。

流れとしては、`webdriver.Chrome()`でブラウザを立ち上げて、`browser`という変数に格納し、この `browser`に対して、メソッドを使用していきます。

通常モードでは、`webdriver.Chrome()`を実行後に別ウィンドウでブラウザが立ち上がります。
Headless モードは、この自動で立ち上がるブラウザを非表示の状態でコードを実行していくことを意味します。

通常モードの場合は、以下の Gif を参考にしてください。
「ブラウザの立ち上げ > Google にアクセス > **株式会社キカガク**を自動入力 > ブラウザを閉じる」 までの処理を行っています。

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/c7b93436-6f41-2b6d-b7e5-f46a663c3089.gif)


### 確認
最後に環境構築ができているか、下記のコードを実行して確認します。私と同じようにソースコードが取得できていれば問題ありません。

```python:ソースコードを取得
browser.get('https://scraping-for-beginner.herokuapp.com/')
print(browser.page_source) 
```
![source_get.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/b4ba19f3-13c7-2627-9ecd-7ad75d622221.gif)

確認ができたら、ブラウザを閉じることを忘れないようにしましょう。

```python:ブラウザを閉じる
browser.quit()
```

## 実践
環境構築ができたので、[こちら](https://scraping-for-beginner.herokuapp.com/)の Web サイトでスクレイピングを行っていきましょう。

▶[Web スクレイピング入門](https://scraping-for-beginner.herokuapp.com/)

※ 本サイトは、サイトの所有者に事前に許可を得てアクセスしておりますので、皆様も安心してご使用いただけます。
ただし、繰り返しアクセスをしてしまうとサイトに負荷がかかってしまうため、アクセスのしすぎにはご注意ください。

### 実装の流れ
実装は、以下の３つの流れで行います。

1. サイトの構成を把握
2. 特定のデータを取得
3. CSVファイルで出力

ここでは、抽象的に挙げましたが各章で詳しく解説していくので、今はイメージを持つだけで問題ありません。

### 1. サイトの構成を把握
環境構築ができたので、すぐにスクレイピングを行いたいところですが、まずはサイトの構成を把握するところから行いましょう。
理由としては、作業が途中で進まなくなったり非効率な順番でデータを取得してします可能性があるからです。
このようなことを避ける為にも、一番始めにサイトの構成をしっかりと把握しておきます。

それでは、先ほどのサイトのトップページから構成を確認していきます。（https://scraping-for-beginner.herokuapp.com/）

![構成1_修正.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/33e82c56-5efc-60e7-8c09-93f6eea86540.jpeg)

更に細かく分けることはできると思いますが、今回は 4 つに分けて考えていきます。

① ヘッダー
② ページ内リンク
③ メインテキスト
④ ページ内リンクボタン

ページ内リンクがあることがわかったので、 **④ のランキングボタン**をクリックしてみます。

クリック後は、「観光地ランキングのページ」に遷移していることがわかりました。
それではこのページの構成も確認していきたいと思います。
![ランキング.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/88fc13c8-5149-75ce-7397-6320322a98fb.jpeg)

① 現在のURL：https://scraping-for-beginner.herokuapp.com/ranking/
② ヘッダー
③ タイトル
④ ランキング + 観光地名
⑤ 総合評価（ ５ 段階）
⑥ 詳細の評価とコメント

ランキングページは、情報量が多く細かい構成になっていることがわかります。ですが、このように事前に構成を確認することで、どのデータが取得できるかを把握することができます。

### 2. 特定のデータを取得
サイトの構成を把握が把握できたので、本題のスクレイピングを行います。
今回は、「観光地名・ 総合評価」の ２ つの要素を取得したいと思います。

要素を指定する場合には、HTML で対応付けされている**属性（ id や class ）**を**デベロッパーツール**で確認する必要があります。

デベロッパーツールは Google Chrome のデバッグツールで、 Web サイトの HTML 構成や表示スピードなどを確認できます。

「右クリック > 検証」もしくは、「ブラウザのメニュー > その他のツール > デベロッパーツール」で開くことができます。



![devtool.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/81ae2d66-a813-a0ec-991f-0daccc26bb71.gif)



#### 観光地名を取得
まずは、トップページにアクセスが必要です。

Selenium はブラウザの自動操作ができるので、トップページにアクセスしてランキングボタンをクリックしてもいいですが、ランキングページの URL がわかっているので、今回は直接アクセスしましょう。

アクセスするためには、**webdriver.Chrome** オブジェクトを生成して、`browser.get()` の引数に URL を渡すことでアクセスできます。その URL のソースコードを `print()` で表示させます。

先ほどブラウザを閉じているので、立ち上げるところから始めていきます。

```python:ブラウザ立ち上げ
#ヘッドレスモードでオブジェクト生成
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
browser = webdriver.Chrome('chromedriver',options=options)

#urlを変数に格納
url = 'https://scraping-for-beginner.herokuapp.com/ranking/'
#アクセス
browser.get(url)
print(browser.page_source)
```

![page_source.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/6677e248-32e7-b37d-9bf3-13c94df400e5.gif)

ソースコードが出力されてアクセスできたことが確認できたので、「観光地 1」という観光地名を取得していきます。

デベロッパーツールを開き、 ① をクリックすると要素を選択できるようになります。今回は観光地名の属性を確認したいので ② 付近をクリックすると、③ で属性を確認することができます。

観光地 1 は、class 名が u_title の div タグ内に記述されていることがわかりました。

![構成1_観光地名.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/549fdaff-dc45-f29a-bae0-8df3d1432d05.jpeg)



`browser.find_element_by_class_name()` を使用して u_title クラス内の要素を取得します。

```python:観光地名の取得
name_elem = browser.find_element_by_class_name('u_title')
name_elem.text
```

![観光地1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/78a70183-8bf2-cede-c3f2-72a86c366c03.jpeg)

中身を確認すると、「ランキング\n観光地名」で格納されているので、`split()` を使用して観光地名のみを取得します。

```python:観光地名のみを取得
elem = name_elem.text.split('\n')
print(elem)
elem = elem[1]
print(elem)
# elem = name_elem.text.split('\n')[1] ←1行でも書けます
```
![観光地スプリット.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/9903cf92-0deb-a251-f83d-6649f455b8e2.jpeg)


elem を確認すると、観光地名のみを取得できていることが確認できました。

次は複数の観光地名を取得していきます。
同じ class 名の要素を複数取得する場合には、`browser.find_elements_by_class_name( )` のように複数形（element<font color="red">s</font>）を使用します。

```python:複数取得して要素数を確認
elems = browser.find_elements_by_class_name('u_title')
print(len(elems))
```
![スクリーンショット 2021-02-10 23.37.32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/1e7edc78-b00d-28be-7e48-9b1d387c34b4.jpeg)

```python:最初の要素を確認
elems[0].text
```

![スクリーンショット 2021-02-10 23.37.39.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/49592a02-8711-86d8-58e2-ae8d2e032b17.jpeg)


先ほどと同じ用に、観光地名のみを取得します。

```python:観光地名のみを取得
name_elems = []
for e in elems:
  name_elems.append(e.text.split('\n')[1])

# name_elem = [e.text.split('\n')[1] for e in elems] ←1行でも書けます
print(name_elems)
```

![観光地名_複数.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/77664a60-5a4c-6a1b-9939-3ac511828095.jpeg)

ページ内の 10 件の観光地名を取得することができました。

#### 総合評価
次に総合評価を取得していきます。先ほどと同じように、デベロッパーツールを使用して確認します。

総合評価は、 class 名が u_rankBox の div タグ内に記述されていることがわかります。
![総合評価.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/1e311a06-9cfd-05ee-5bf0-8f60386a037d.jpeg)


それでは、取得していきましょう。取得方法は先ほどと同じなので、できる方はこれまでのコードを参考に自分で挑戦してみてください。

```python:総合評価を取得
evl_elem = browser.find_element_by_class_name('u_rankBox')
evl_elem.text
```

総合評価が取得できていることが確認できたので、複数取得していきます。

```python:複数取得して要素数を確認
elems = browser.find_elements_by_class_name('u_rankBox')
print(len(elems))
```
![スクリーンショット 2021-02-10 23.44.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/3a5d84ae-f8de-e2cb-9c3e-46d5a723b065.jpeg)

観光地名と同じように 10 件取得できました。あとは、for 文で各要素のテキストを取得します。


```python:テキストのみを取得
evl_elems = [e.text for e in elems]
print(evl_elems)
```

![スクリーンショット 2021-02-10 23.44.25.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/56436aec-2f3e-b92c-8d64-aa1f604dbf89.jpeg)

これで 2 つの要素は取得できたので、`browser` を閉じます。

```python:ブラウザを閉じる
browser.quit()
```

#### 【補足】ランキングボタンをクリック
今回はランキングページに直接アクセスしましたが、Selenium は**ブラウザを自動操作**することができるので、トップページのランキングボタンをクリックしてページ遷移後にデータを取得することもできます。

まずは、トップページへアクセスをします。クリック後に遷移できているかを確認したいので現在の URL を出力しておきます。

```python:Headlessモードでブラウザ立ち上げ
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
browser = webdriver.Chrome('chromedriver',options=options)

#urlを変数に格納
url = 'https://scraping-for-beginner.herokuapp.com/'
#アクセス
browser.get(url)
#現在のURLを確認
print(browser.current_url)
```
![スクリーンショット 2021-02-10 23.06.41.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/b7eb12c3-9f70-d674-845c-d74770f86670.jpeg)

ランキングボタンは、これまでと同じようにデベロッパーツールで確認すると、 id 名が card-action の div タグ内の a タグで記述されていることがわかります。

ランキングボタンクリックの流れは、以下の通りです。

1. 要素を指定
2. インデックスを指定
3. クリック

```python:要素を指定
elem = browser.find_element_by_id('card-action')
buttons = elem.find_elements_by_tag_name('a')
print(len(buttons))
```

![スクリーンショット 2021-02-10 23.05.53.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/1d7dd650-d596-fd7d-e1d7-401d548f30a4.jpeg)

a タグが３つあるので、要素数も３と出力されています。


a タグには属性が無いので、インデックスでランキング要素を指定してクリックします。

```python:インデックスを指定してクリック
buttons[1].click()
#現在のURLを確認
print(browser.current_url)
```

![クリック後.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/be8d382c-7fc3-c6dd-7bdd-ce6dad698fec.jpeg)

URL がランキングページの URL と一致しているので、クリックできていることが確認できました。

### 3. CSV ファイルで出力
最後に取得したデータを CSV 形式で出力していきます。スクレイピングとは関係はありませんが、実務の場合はデータを取得して保存しておかないとデータを利用できないので、CSV 形式で出力していきます。

まずは、Python パッケージの Pandas を使用して、各データを DataFrame に格納します。


```python:DataFrameを作成
import pandas as pd

#カラムを定義
columns = ['観光地名', '総合評価']
#DataFrameを作成
df = pd.DataFrame(columns=columns)

#確認
df
```

各データが格納されている変数を df に格納して、中身の確認をします。

```python:データを格納
df['観光地名'] = name_elems
df['総合評価'] = evl_elems

#上位3件を確認
df.head(3)
```

![df_確認.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/f654b75e-2a01-2642-5125-fc893cb91195.jpeg)


確認もできたので、CSV 形式で出力をします。

```python:CSVで出力
df.to_csv('sample.csv')
```

コードを実行すると、サイドバーに sample.csv が作成できていることがわかります。

![csv出力.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/31057e54-bf56-00c7-112b-0e74fe6935d2.gif)

作成ができたら、 サイドバーの sample.csv にカーソルを合わせて、︙ -> ダウンロードをクリックすると CSV ファイルがダウンロードされます。

![スクリーンショット 2021-02-10 23.31.12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684378/5a74ef0b-3566-af03-7c79-e5f7c69ff816.jpeg)

# おわりに
このように、簡単にスクレイピングから CSV 形式で出力まで行うことができました。
スクレイピングと聞くと難しいイメージを持ちますが、実際のコードを書いてみると似たようなコードが多く覚えることも少ないと思います。

次回の記事では、ログイン処理やタイムアウトなどの処理も含めたデータの取得をやっていきましょう！

# お知らせ
![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_130181_36206be5-499a-66df-a401-a28a66f2b3d2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314351/ae15a062-1ce3-d038-f4e3-dda17f498b9d.jpeg)

動画を通じて、ディープラーニングが一から学習できる大人気コースの脱ブラックボックスが無料になりました。

**手書きの数学とハンズオン形式のプログラミング**によって、初学者でも安心して一から学習できます。

機械学習やディープラーニングを基礎から実践を学びたい方は、ぜひ教材の１つとしてご利用ください！

[▶︎ 脱ブラックボックスコースを無料で学び始めたい方はこちら！](https://www.kikagaku.ai/)
<br>
<br>