Dockerを使って動的なサイトをスクレイピングしてみよう

# はじめに
近畿大学 Advent Calendar 2019の13日目の記事です.

はじめに注意なのですが, 基本的にスクレイピングは必要がないならするべきではない最終手段です. 今回はQiitaのタグランキングをスクレイピングしますが, Qiitaにはapiが存在しておりそこにタグランキングを取得するapiが存在しなかった(2019年12/8現在)のでスクレイピングをしました.もしあなたが欲しい情報がapiを用いて取得できるならばapiで取得しましょう. また, スクレイピングする際は接続時に待機時間を設け, 接続時間を空けましょう.

## 必要なもの

Docker

自分が使ってるバージョンは以下.
`Docker version 19.03.5, build 633a0ea`

## docker-compose.yml

```docekr-compsoe.yml
version: '3'

services:
    selenium-hub:
        image: selenium/hub
        container_name: selenium-hub
        ports:
          - "4444:4444"
      
    chrome:
        image: selenium/node-chrome-debug
        depends_on:
          - selenium-hub
        environment:
          - HUB_PORT_4444_TCP_ADDR=selenium-hub 
          - HUB_PORT_4444_TCP_PORT=4444
    
    python:
        build: .
        container_name: python
        volumes: 
            - .:/workspace
        command: /bin/bash
        tty: true
        stdin_open: true
```

```Dockerfile
FROM python:3.7
WORKDIR /workspace

RUN pip install \
    selenium \
    beautifulsoup4
```

### dockerfileとcompose説明

今さらdokcer-composeやdockerfileとは何かというのは書かないのでわからなければ[Docker上でElixirのPhoenixとPostgreSQLを使ってみた](https://qiita.com/niya1123/items/192b4f93878830c2ede5)という記事に詳しめに書いているのでそちらを参考にしてください.

では早速docker-compse.ymlから説明します.

動的サイトをスクレイピングするには現在では基本`Selenium`一択だと思います.

`selenium/hub`と`selenium/node-chrome-debug`というイメージを使ってコンテナを作成します.
ここで`selenium/node-chrome-debug`のほうで`environment`が設定されています.これがなければスクレイピングできないので注意しましょう.

Dockerfileのほうではpythonの環境を構築しています.`RUN`コマンドで必要なライブラリをDLしています.

これらのファイルと下にあるコードを同一階層上に配置して, `docker-compose up -d --build`を実行すればコンテナが立ち上がりあます.

## コード例

```python
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from bs4 import BeautifulSoup
import pprint

class QiitaGetRanking():
    """
    Qiitaからランキングデータを取得するクラス.
    """

    def get_tag_ranking(self, browser: webdriver) -> dict:
        """
        Qiitaからタグランキングに関する情報を取得する関数.
        
        Parameters
        ----------
        browser: webdrive
            スクレイピングするためのwebdriverのオブジェクト
        
        Returns
        -------
        tag_ranking_data: dict
            タグランキングを収めた辞書オブジェクト.
        """
        html = browser.page_source.encode('utf-8')
        soup = BeautifulSoup(html, "html.parser")
        ra_tag_names = soup.find_all(class_='ra-Tag_name pr-1')
        tag_ranking_data = {}
        for i, ra_tag_name in enumerate(ra_tag_names):
            tag_ranking_data[i+1] = [ra_tag_name.text, 
            'https://qiita.com/tags/%s'%(ra_tag_name.text.lower())]
        return tag_ranking_data

if __name__ == "__main__":
    """
    main文. browserはhtmlの取得が終わり次第閉じること.エラーが出てきたときも同様.
    """
    
    try:
        browser = webdriver.Remote(
            command_executor='http://selenium-hub:4444/wd/hub',
            desired_capabilities=DesiredCapabilities.CHROME)
        print("start scrape")
        browser.get('https://qiita.com')
        # javascriptが全て読み込まれるまで待機. 15秒経っても読み込みが終わらなければタイムアウト判定.
        WebDriverWait(browser, 15).until(EC.presence_of_all_elements_located)
        print("generate object")
        qgr = QiitaGetRanking()
        ranking_data = qgr.get_tag_ranking(browser)
        browser.close()
        browser.quit()
        pprint.pprint(ranking_data)
    except:
        browser.close()
        browser.quit()
```
コードはいたってシンプルだと思います.dockerでスクレイピングする際はseleniumサーバーを建ててスクレイピングした方がubuntu上にselenium環境を構築するより楽だし軽いです.

webdrriver.Remoteに関しては[2.5. リモートWebDriverでSeleniumを使用する](https://kurozumi.github.io/selenium-python/getting-started.html#using-selenium-with-remote-webdriver)を参考にしてみてください.

## 実行結果
`docker ps`で3つのコンテナが建っていることを確認してから,`docker exec -it python python qiita.py`でプログラムを実行してください.

```
start scrape
generate object
{1: ['Python', 'https://qiita.com/tags/python'],
 2: ['JavaScript', 'https://qiita.com/tags/javascript'],
 3: ['AWS', 'https://qiita.com/tags/aws'],
 4: ['Rails', 'https://qiita.com/tags/rails'],
 5: ['Ruby', 'https://qiita.com/tags/ruby'],
 6: ['初心者', 'https://qiita.com/tags/初心者'],
 7: ['Docker', 'https://qiita.com/tags/docker'],
 8: ['PHP', 'https://qiita.com/tags/php'],
 9: ['Vue.js', 'https://qiita.com/tags/vue.js'],
 10: ['Go', 'https://qiita.com/tags/go']}
```

こんな感じで表示されていたら完全勝利です.お疲れさまでした.

# さいごに
今回はDockerを用いて動的サイトをスクレイピングする方法を紹介しました.皆さんも制限の範囲内でスクレイピングを楽しみましょう！