【Python / Chrome】スクレイピングの基本設定と操作

# はじめに
今までは、VBAでスクレイピングをしていましたが、Internet Explorerがいつまで使えるか分かりません。
それで、**Python**で**Chrome**を対象としたスクレイピングを始めてみました。
環境は**Windows**です。

今更な内容ですが、押さえて置いた方がよいであろう基本事項（多分、数か月後には忘れてしまう内容）を、個人的な備忘も兼ねて記しておきます。

**＜目次＞**
[1. seleniumのインストール](#1-seleniumのインストール)
[2. WebDriverのダウンロード](#2-webdriverのダウンロード)
[3. ソースコードの記述](#3-ソースコードの記述)

# 1. seleniumのインストール

まず、Pythonに[selenium](https://pypi.org/project/selenium/)というブラウザを操作するパッケージをインストールします。

次のように、コマンドプロンプトから`py -m pip install selenium`というコマンドを打つことでインストールできます。

```:コマンドプロンプト
>py -m pip install selenium
Collecting selenium
  Downloading selenium-3.141.0-py2.py3-none-any.whl (904 kB)
     |████████████████████████████████| 904 kB 1.1 MB/s
Collecting urllib3
  Downloading urllib3-1.25.11-py2.py3-none-any.whl (127 kB)
     |████████████████████████████████| 127 kB 939 kB/s
Installing collected packages: urllib3, selenium
Successfully installed selenium-3.141.0 urllib3-1.25.11
```

Python自体のインストール方法なども含めて、詳しくは、[こちら](https://qiita.com/t-yama-3/items/8fb0a99357f7766a70fe)に書いています。

# 2. WebDriverのダウンロード

次に、ブラウザの種類に応じた**WebDriver**が必要になります。

●参考書籍
[中嶋英勝『PythonでExcel、メール、Webを自動化する本』SBクリエイティブ](https://www.amazon.co.jp/Python%E3%81%A7Excel%E3%80%81%E3%83%A1%E3%83%BC%E3%83%AB%E3%80%81Web%E3%82%92%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%99%E3%82%8B%E6%9C%AC-%E4%B8%AD%E5%B6%8B%E8%8B%B1%E5%8B%9D/dp/4815606390)

## 2-1. ダウンロードサイト

次のような、[ChromeDriverのダウンロードページ](https://sites.google.com/chromium.org/driver/downloads?authuser=0)を開きます。
![2020-11-01 152516.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/262316c5-a428-0450-81c0-90564d955a6c.png)
上記の赤枠には、3つのバージョン（87, 86, 85）のChromeDriverがあります。
このうち、現在使用しているChromeのバージョンに一致するものをダウンロードすることになります（次項参照）。

なお、ブラウザごとのWebDriverのリンクは「[PyPI / selenium](https://pypi.org/project/selenium/)」の`Driver`の項から確認できます。

## 2-2. Chromeのバージョン確認

Chromeブラウザメニューの「ヘルプ(H)」から「Google Chrome について(G)」を開くとバージョンが確認できます。
![2020-11-01 152918.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/96448182-a6c4-d082-7feb-263641cf6df0.png)
私の環境では、Chromeのバージョンは`86`なので、先ほどのダウンロードサイトから、これに対応した`ChromeDriver 86.0.4240.22`をクリックします。

## 2-3.  WebDriverの取得

次のような画面が表示されたら、Windows用のChromeDriverをクリックしてインストールします。
![2020-11-01 153622.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/f417d1d5-ecfd-e4b8-530a-4c1694f8e808.png)
ダウンロードしたファイルを解凍すると、次のように`chromedriver.exe`というWebDriverを得ることができます。
![2020-11-01 185846.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/0dc60278-e5fb-6ecb-8f28-be56cab4ed7a.png)
このDriverを、Pythonのソースコードファイルと同じフォルダに置いて使用することになります（違うフォルダに置いて、ソースコードでパスの指定をしても良い。）。

# 3. ソースコードの記述

yahooのサイトを開いて検索を実行するコードを書くと次のようになります。

```Test01.py
import time
from selenium import webdriver

driver = webdriver.Chrome() # WebDriverのインスタンスを作成
driver.get('https://www.yahoo.co.jp/') # URLを指定してブラウザを開く
time.sleep(2) # 2秒待機
search_box = driver.find_element_by_name('p') # name属性で検索ボックスを特定
search_box.send_keys('スクレイピング') # 検索ボックスにテキストを入力
search_box.submit() # 検索文言の送信（検索ボタンを押すのと同じ）
time.sleep(2) # 2秒待機
driver.quit() # ブラウザを閉じる
```

これは、[ChromeDriverのサイト](http://chromedriver.chromium.org/getting-started)にあるコードを若干修正したものです。
実行すれば、ブラウザ（Chrome）が立ち上がり、yahooのサイトから「スクレイピング」の文言の検索を実行します。

以下、簡単に解説を残しておきます。

## 3-1. ライブラリのインポート

```Sample.py
import time
from selenium import webdriver
```

基本は`import [ライブラリ名]`の振合いで記述します。

1行目は、標準ライブラリの`time`をインポートしています。
2行目は、先ほどインストールしたパッケージ`selenium`から`webdriver`というライブラリをインポートしています。

## 3-2. WebDriverのインスタンスの作成

### 3-2-1. ソースコードと同じフォルダにドライバを保存している場合
ソースコードと同じフォルダにChromeDriverを保存している場合は次のように記述すれば、WebDriverのインスタンス作成ができます。

```Sample.py
driver = webdriver.Chrome()
```

### 3-2-2. ソースコードと異なるフォルダにドライバを保存している場合

一つ下のディレクトリ（フォルダ）にドライバーを保存している場合は、次のように記述します。

```Sample.py
driver = webdriver.Chrome('Driver/chromedriver')
```

上記の`Driver`はディレクトリ名（フォルダ名）です。

### 3-2-3. インスタンスの変数名について

なお、（当然ですが）変数の`driver`は何でも構いません。
次のように、変数を`d`にしても大丈夫です・

```Sample.py
d = webdriver.Chrome()
```

## 3-3. URLを指定してブラウザを開く

次のように、`[インスタンス名].get([URL名])`の記述で、指定したサイトを開くことができます。

```Sample.py
driver.get('https://www.yahoo.co.jp/')
```

## 3-4. ノードの取得

### 3-4-1. HTMLの確認

検索を実行するために、次のような`検索文言を入力するテキストボックスの場所`を読み取る必要があります。
![2020-11-03 003431.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/c0b53c12-3b6f-0b49-74d2-90d0e99198e1.png)
これは、WEBページのHTMLを見て取得することになります。
テキストボックスのHTMLを確認するには、その場所を右クリックして「検証(I)」を選択します。
![2020-11-03 003743.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/7e3d3919-db19-b394-3178-35e264113a3a.png)
すると右側に、サイトのHTMLが表示されます。
そのうち、青くなっているところが該当箇所のHTMLとなります。
![2020-11-03 013616.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/4b1937fd-fca8-12be-74f6-459e489d6ce3.png)
このHTMLでは、`input`というタグの中に、`type`、`class`、`name`などの属性値が指定されています。
これらのタグ名、属性値を手がかりにして、必要な部分（ノード）を指定することになります。

タグ名や属性値は、同じものが重複する場合もあるので、まずは一意となる（ページ中でそれ一つしか無い）ものを探し出します。
確認すると次の`class`と`name`がページ中で1つしかないことが判明しました。

`class="_1wsoZ5fswvzAoNYvIJgrU4"`
`name="p"`

### 3-4-2. ノードを取得するソースコード

ここでは、よりシンプルな`name`の属性値を用いて、次のようなコードで必要な部分（ノード）を取得します。
`search_box`は変数なので、別名でも構いません。

```Sample.py
search_box = driver.find_element_by_name('p')
```

`name`の属性値でノードを取得する場合は、`[インスタンス名].find_element_by_name([属性値])`の振り合いで記述します。

なお、`class`の属性値で取得する場合は、次のように書きます。

```Sample.py
search_box = driver.find_element_by_class_name('_1wsoZ5fswvzAoNYvIJgrU4')
```

### 3-4-3. ノード取得のためのメソッド

ノードを取得するためのメソッドは、`name`、`class`以外にもいくつか用意されています（[このサイト](https://selenium-python.readthedocs.io/locating-elements.html)を参照）。

#### 3-4-3-1. ノードを単数で取得する場合

|メソッド|取得対象|
|---|---|
|find_element_by_id|id名（属性値）|
|find_element_by_name|name名（属性値）|
|find_element_by_xpath|XPathで取得|
|find_element_by_link_text|リンクテキストで取得|
|find_element_by_partial_link_text|リンクテキストの一部で取得|
|find_element_by_tag_name|タグ名（要素）|
|find_element_by_class_name|class名（属性値）|
|find_element_by_css_selector|セレクタで取得|

単数で取得する場合は、同名のものがあっても、最初に見つかったノードのみが取得されます。

なお、私のようにXPathをよく知らない方は、こちらの記事「[クローラ作成に必須！XPATHの記法まとめ](https://qiita.com/rllllho/items/cb1187cec0fb17fc650a)」を参照してください。使いこなせたらかなり便利だと思います。

#### 3-4-3-2. ノードを複数（リスト）で取得する場合

|メソッド|取得対象|
|---|---|
|find_elements_by_name|id名（属性値）|
|find_elements_by_xpath|name名（属性値）|
|find_elements_by_link_text|リンクテキストで取得|
|find_elements_by_partial_link_text|リンクテキストの一部で取得|
|find_elements_by_tag_name|タグ名（要素）|
|find_elements_by_class_name|class名（属性値）|
|find_elements_by_css_selector|セレクタで取得|

単数で取得する場合は、同名の全てのノードが、リスト形式で取得されます。

本来のHTMLの記述には、`id名`は一つのページの中に1つ、`class名`、`name名`は複数でも構わないなどのルールがありますが、id名が複数あるサイトもあるため、`find_elements_by_name`などの複数（リスト）取得のメソッドが用意されています。

複数取得の場合はリスト形式でノードを取得していることから、次のように要素番号までを指定する必要があります。

```Sample.py
search_box = driver.find_elements_by_name('p')[0]
```

### 3-4-4. 【参考】ノードのノード（子要素以下）を取得する

ノードを取得する際に、一意のid名などがあれば簡単にコードが書けますが、そう都合良くいかない場合もあります。
そのような場合には、絞り込み検索をするような具合で、一旦、大きめの範囲のノードを指定して、その子要素（孫要素）のノードを指定するという方法をとることが多く発生します。

ノードのノードを取得する方法は、次のように単純にメソッドを連結することで実現できます。

```Sample.py
search_box = driver.find_element_by_tag_name('fieldset').find_element_by_tag_name('input')
```

なお、変数を分けて書くことも可能です（下記）。

```Sample.py
search_box1 = driver.find_element_by_tag_name('fieldset')
search_box = search_box1.find_element_by_tag_name('input')
```

2回目の絞り込みでは、子要素以下のノードが対象となります。

## 3-5. 検索ボックスにテキストを入力する

検索ボックスにテキストを入力するには、`send_keys`メソッドを使って次のように記載します。
ここでは「スクレイピング」という文言をテキストボックスに入力しています。

```Sample.py
search_box.send_keys('スクレイピング')

```

## 3-6. 検索の実行

次のように`submit`メソッドを使用することで、検索フォームに入力したテキストをWEBサイトのサーバに送信する（つまり検索を実行する）ことができます。

```Sample.py
search_box.submit()
```

これは、HTMLのフォームデータをサーバに送信する操作をしていることになります。

なお、次のように、単純に「検索ボタンをクリックする」という命令を実行しても同じ結果を得ることができます。

```Sample.py
search_box = driver.find_element_by_class_name('PHOgFibMkQJ6zcDBLbga8').click()
```

これは`検索ボタン`についてクラス名でノードを取得して、`click`メソッドにより検索ボタンのクリック操作を行っていることになります。

## 3-7. ブラウザを閉じる

次のコードで、開いたブラウザを閉じることができます。

```Sample.py
driver.quit()
```

# さいごに

以上のところが`基本の基本`というところでしょうか。

これを書きながら、`innerText`とか`outerHTML`とかはどうやって取得するのだろうかと考えましたが、この辺も普通にメソッドが用意されていました。

Pythonのスクレイピングは、多くの方が利用されているので、結構使いやすくなっているように思われます。