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
FROM python:3.7
WORKDIR /workspace

RUN pip install \
    selenium \
    beautifulsoup4

dockerfileとcompose説明

今さらdokcer-composeやdockerfileとは何かというのは書かないのでわからなければDocker上でElixirのPhoenixとPostgreSQLを使ってみたという記事に詳しめに書いているのでそちらを参考にしてください.

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

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

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

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

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

コード例

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を使用するを参考にしてみてください.

実行結果

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を用いて動的サイトをスクレイピングする方法を紹介しました.皆さんも制限の範囲内でスクレイピングを楽しみましょう!