Seleniumで要ログインページをスクレイピングをした話（初心者目線）

#SeleniumをPython3で動かして要ログインページをスクレイピングした話
##1.目次
1. 目次
2. 筆者について
3. スクレイピングとは
4. Seleniumとは
5. どうしてSelenium
6. コーディング
7. 結果とまとめ

##2.筆者について
はじめまして、unotoviveです。エンジニアリングに触れたのは約2年前、都内の某大学の学生をやっております。普段はWeb系(フロントエンド系)の開発をしていますが全体的に初心者です。この記事も割と初心者向けに書きます。尚初心者ゆえに多々お見苦しいコードやアルゴリズム等あると思うのでご指摘いただけると自信の勉強になり幸いです。
よろしくお願いいたします。

##3.スクレイピングとは
>ウェブスクレイピング（英: Web scraping）とは、ウェブサイトから情報を抽出するコンピュータソフトウェア技術のこと。ウェブ・クローラーあるいはウェブ・スパイダーとも呼ばれる。 通常このようなソフトウェアプログラムは低レベルのHTTPを実装することで、もしくはウェブブラウザを埋め込むことによって、WWWのコンテンツを取得する。(Wikipediaより)

はい、要はとあるサイトの情報を自動で収集なり保存なりをする技術のことをスクレイピングと言うみたいです。

スクレイピングはWebのコンテンツを収集しているという点で著作権法などの法律に関わってきてしまう行為なので、利用目的等に注意しながら行ったほうが良いみたいです。

[スクレイピングと法律](https://it-bengosi.com/blog/scraping/)

今回は某大学の学籍管理システムからシラバスの情報を保存したくてスクレイピングに手を出しました。

##4.Seleniumとは
SeleniumはWebでの開発等において自動テスト等に利用されるライブラリです。ブラウザでの操作を自動で行ってくれるという物で様々な言語に対応しているみたいです(Python,Java,js,Ruby,etc...)

##5.どうしてSelenium
BeautifulSoup等のよくスクレイピングで利用されるライブラリとの違いは、（個人的感想ですが）ユーザがより普段の操作に近いイメージで設定し動作するという所ではないかと思います。
というのもWebテストツールということで実際の操作のようなものを定義してそれを実行します。
そのため
ログインページからログイン→メニューから目的のページに移動→データを取得
といったような操作を要する物をスクレイピングする時に初心者にはとてもありがたい直感仕様なのかなと思います。
ログインやフォームへの入力などが必要で、直感的に行いたい！という場合はSeleniumが良いのではないでしょうか。

今回は以下の操作を行いました。

- ページにアクセス
- **IDとpasswordを入力してログインボタンをクリック**
- トップ画面でメニューに**マウスホバー**
- 表示されたメニューから選択して画面遷移
- **検索フォームでセレクトボックスから値を選択して検索**
- 複数の結果が出るのでそれぞれをクリックして繰り返し以下の動作
    - テーブルデータをExcel形式で列として保存
    - **戻るボタンをクリックして一覧に戻る**
- 全部終わったら終了

Seleniumで太字の部分が直感的に行えるのではないかなと思います。

##6.コーディング
###使用したもの
- Python3
- Chromedriver(Webdriverというブラウザの素らしい)
- Selenium

.pyファイルに記述します。
###準備
まず必要なものをimportします。

```python:selenium.py
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select
from xlwt import Workbook
```

最後のはExcelワークシートを扱うための奴です。
インストールしていない場合はそれぞれ`pip install`しましょう。

続いてChromedriverによってブラウザを起動します（実際にGUIで起動するわけではありません。）

```python:selenium.py
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome('chromedriver.exe',chrome_options=options)
driver.get(URL)
```

chromedriver.exeはダウンロードしてきた物のパスを指定します。Windowsなのでexeですが[こちら](http://chromedriver.chromium.org/downloads)からそれぞれのOSに対応したものをダウンロードできます。

URLの部分はスクレイピングしたいページのトップページ（ログインが必要な場合はログインページ）を指定します。
書き出し方法は今回はExcelを使っているので念のためそちらも記述します。ワークシートの初期化とシートの追加です。任意の名前をシートに付けます。

```python:selenium.py
wb = Workbook()
ws = wb.add_sheet(sheetname)
```

***
###ログイン
早速ログインしたいと思います。
作業としては実際にそのページを訪れ、ログインフォームのinput要素のid等を探します。
Seleniumではidやタグやcssセレクタ、XPathなど様々な物で要素の取得が可能です。
要素の取得方法について詳しくは→[こちら](http://www.seleniumqref.com/api/webdriver_gyaku.html)　自分はこのサイトをガン見しながらやりました。どうやらelementをelementsにすると複数取得できるようです。

```python:selenium.py
driver.find_element_by_id('form1UserId').send_keys(ID)
driver.find_element_by_id('form1Password').send_keys(PASS)
driver.find_element_by_id('form1Login').click()
print('ログイン成功')
```
要素を指定して.sendkey()で値を入力、.click()で要素をクリックします。とても直感的ですね。
ここでは`form1UserID` `form1Password`（それぞれのinput要素）に値を入力し、`form1Login`（ログインボタン）をクリックしています。この動作が成功すると次に進みます。
要素が見つからないなど何らかのエラーが発生すると止まってしまいますが、例えばパスワードが違います等で画面遷移をすると一連の動作は完了したという判定になるためここでのエラーは発生しません。
***
###マウス操作
続いてはマウスホバーでメニューを表示してそこから移動します。

```python:selenium.py
actions = ActionChains(driver)
actions.move_to_element(driver.find_element_by_id('menu')).perform()
driver.find_element_by_id('menuContent').click()
```
ActionChainsという物をDriverで定義し、そこからマウスのアクションを実行します。
`action.move_to_element(driver.find_element_by_id('menu')).perform()`では上で定義したactionに対してマウスをmenuというidの要素まで移動する、という操作を実行しています。最後のperformが無いと実行しないみたいです。

さて、これで仮想のブラウザ上ではメニューが開いている状態なのであとは目的のメニューをクリックするだけです。
ログイン時と同じように`driver.find_element_by_id('menuContent').click()`でボタンをクリックして画面遷移します。
***
###セレクトボックス
こんどは検索画面なんです。先のログイン時と違う所はフォームにセレクトボックスがあります。
セレクトボックスはそれを定義して要素を代入してからselect_byで操作します。

```python:selenium.py
selector1 = Select(driver.find_element_by_id('selectBox1'))
selector2 = Select(driver.find_element_by_id('selectBox2'))
selector1.select_by_value("X")
selector2.select_by_value("Y")
driver.find_element_by_id('search').click()
```
Select(要素)でSelectを定義します。それに対して今回は選択肢のValueで選択するので`.select_by_value("X")`を使用します。(Xは任意の選択肢)
最後に今まで同様検索ボタンをクリックします。
***
###要素の取得と繰り返し
ここで検索結果がズラーと表示されます。何個あるかは結果によりまちまちなのでその回数繰り返し動作するプログラムを記述します。

```python:selenium.py
table = driver.find_element_by_xpath("html/body/div/div/form[3]/table/tbody/tr[4]/td[2]/b/table/tbody")
trs = table.find_elements_by_class_name("rowClass1")
print(len(trs))
```
今回実行したサイトは古いのか~~頭のおかしい構造の~~階層の深いhtmlでした。目的のtr要素が格納されている要素（今回はtbody）を最初にtableとしておきます。これはXPathで取得しましたが先のようにid等でも取得できる(はず)です。
その後`find_elements_by_class_name("rowClass")`でrowClassというclassの付いた要素をすべて取り出しています。ポイントはelementsになっているところで、当てはまる要素をすべてリスト型で返してくれるようです。（[詳細はこちら](https://kurozumi.github.io/selenium-python/locating-elements.html)）
帰ってきたリストの要素数を数えることで何個のtrが存在するかがわかります。
***
###テキストの取得と書き込み
ここまでくれば後は繰り返しページにアクセスして内容を記録するだけです。

```python:selenium.py
clasnum=0
for i in range(len(trs)):
    clasnum+=1;
    driver.find_element_by_id('form:'+str(i)+':element').click()


    print("nowCaptureing"+str(i))
    table2 = driver.find_element_by_xpath("html/body/div/div/form[3]/table/tbody/tr/td[2]/table/tbody/tr[2]/td/div/table/tbody/tr/td/table/tbody")
    trs = table2.find_elements_by_tag_name("td")
    number = 0

    for td in trs:
        print(td.text)
        number+=1
        ws.write(clasnum,number,td.text)

    driver.find_element_by_id('back').click()
```
forでリストの要素数繰り返しを行います。幸いにも出てきたリストのidが上からform:1:element,form:2:elementとなっているようだったので指定してクリックします。
これでデータを取得したいページにたどり着きました。
このページに存在するtableのデータ(td要素部分)をエクセルのワークシートに記述します。
table2に目的のtable要素を代入し、その中から`.find_elements_by_tag_name("td")`でtd要素のリストを取得します。

ここでまたこのリストの回数繰り返しです。今回はリストからtd要素をそれぞれ取り出してそのまま利用したいのでobjectのforループ？（名前を知らない）を利用します。trsの中からtdを取り出してfor内で利用できます。
その要素の中のtextを取得するには.textを利用します(.text()ではない)。これで目的のテキストが順番に取り出されるようになりました。
`ws.srite`で(行,列,text)で最初に定義したexcelファイルのセルにtextを書き込んでいきます。
(numberとclasnumはexcelの行と列を指定するための変数です。)

一ページ内ですべての要素を書き込み終わったら一覧ページに戻ります。戻るボタンをクリックしています。
***
###終わり
一通りの作業が終了したので最後にワークシートを保存してDriverを終了します。

```python:serenium.py
wb.save("data.xls")
driver.quit()
```
***

以上が今回行ったSeleniumによるスクレイピングの一通りの流れとなります。
##結果とまとめ
###成功しました。
はい。じゃなきゃ記事にしません。

今回はフォームへの入力、クリック、マウス操作を使用しましたが、大体のサイトはこのぐらいの操作で使えるのではないかと思います。SeleniumAPIはとても優秀で他にも結構なことが出来るみたいので調べてみると面白いかもしれません。
またSeleniumで画面遷移等を行いbeautifulSoupを使うなんて事例もあるみたいです。

今回のファイルは[Github](https://github.com/unotovive/UNIPA-Scrapar)にアップロードしてありますが、ここで解説したものよりも汚く、より実際に今回自分がスクレイピングを行ったサイトで使うようにいろいろな部分が書き換わっていますのでご覧になる際も参考程度にお願いいたします。

自分も初心者なので初心者目線で丁寧に解説をしたつもりですが何か不備、指摘等ありましたらお気軽にコメントを頂けますと幸いです。
また「いやそんなことせんでもこうやって出来るで」みたいなのも歓迎ですのでよろしくお願いいたします。