Oasistブログ

言語学、エンジニアリング、ライフ記事を気まぐれにお届け

Python + Seleniumでウェブスクレイピング Vol.1 - シンプルなDOM -

f:id:oasist:20210128231504p:plain
ウェブスクレイピング

Contents

1. はじめに

Udemyのトライアルにて、「PythonによるWebスクレイピング〜入門編〜 業務効率化への第一歩」という講座を受講した。
そこで学んだ内容を、以下の3つの記事で共有したい。

  1. Python + Seleniumでウェブスクレイピング Vol.1 - シンプルなDOM -(本投稿)
  2. Python + Seleniumでウェブスクレイピング Vol.2 - より複雑なDOM -
  3. Python + Seleniumでウェブスクレイピング Vol.3 - 画像収集 -

ソースコード全体を参照したい場合は、「5. ソースコード」](https://oasist-blog-ja.hatenablog.jp/entry/web_scraping_in_python_selenium_vol1#contents-5)に飛んで頂きたい。

2. 成果物

講師情報 - Webスクレイピング入門から講師情報を取得し、CSVファイルにエクスポートする。

項目
0 講師名 今西 航平
1 所属企業 株式会社キカガク
2 生年月日 1994年7月15日
3 出身 千葉県
4 趣味 バスケットボール、読書、ガジェット集め

3. 実装

3-1. ログイン

初めに、Google Chromeを立ち上げて特定のURLにアクセスする必要がある。
この一連の処理は TextExtractor クラスのインスタンス生成時に呼び出される。
コンストラクタはURL(https://scraping-for-beginner.herokuapp.com/login_page)を引数に取る。
get メソッドを使うことで指定したURLにアクセス出来るが、あらかじめ webdriver をインポートするのを忘れないように。

TextExtractor#login

def __init__(self, url):
    self.chrome = webdriver.Chrome(executable_path="../exec/chromedriver.exe")
    self.chrome.get(url)

TextExtractor#login はユーザー名とパスワードを引数に取る。
インスペクターを用いれば #username#password#login-btn をDOM中に見つけることができる。

f:id:oasist:20210129113759p:plain
ユーザー名
f:id:oasist:20210129113747p:plain
パスワード
f:id:oasist:20210129113729p:plain
ログインボタン

webdriver によって、特定のID要素を見つけるための find_element_by_id メソッドが利用可能だ。
また、必要なパラメータを入力する send_keys メソッドや、ボタンのクリック操作を行う click も利用することができる。

TextExtractor#login

def login(self, user_name, pwd):
    username = self.chrome.find_element_by_id("username")
    username.send_keys(user_name)
    password = self.chrome.find_element_by_id("password")
    password.send_keys(pwd)
    login = self.chrome.find_element_by_id("login-btn")
    login.click()

ログイン完了後、講師情報のテーブルを参照することができる。

f:id:oasist:20210129115318p:plain
Lecturer Information

3-2. 情報取得

th 要素からはキーを、td 要素から値を取得する。
find_elements_by_tag_name メソッドを使うと、特定タグの要素のリストを得ることができる。

find_element_by_tag_name は特定タグの最初の要素を返す。

特定のタグ要素の配列を得たら、以下の手順で各要素からテキスト情報を取り出す。

  1. 各要素のテキスト情報を挿入するための空のリストを生成する。
  2. for 文でループを回し、各要素の .text でテキスト情報を取り出す。
  3. テキスト情報にエスケープシーケンスが含まれている場合、特定の文字列に置換する。
  4. 手順1で生成したリストにテキスト情報を追加する。

最後に キーと値を返す。

今回はユニットテストを分かりやすくするために {キー: 値} のディクショナリーも返している。

TextExtractor#get_lecturer_info

def get_lecturer_info(self):
    ths = self.chrome.find_elements_by_tag_name("th")
    keys = []
    for th in ths:
        keys.append(th.text)
    tds = self.chrome.find_elements_by_tag_name("td")
    vals = []
    for td in tds:
        if "\n" in td.text:
            vals.append(td.text.replace("\n", "、"))
        else:
            vals.append(td.text)
    profile = {}
    for i in range(len(keys)):
        profile[keys[i]] = vals[i]
    return profile, keys, vals

3-3. CSVエクスポート

TextExtractor#export_csvCSVファイルをエクスポートするためのキー、値、ファイルパスを引数に取る。

  1. DataFrame メソッドで空のデータフレームを生成する(pandas のインポートをあらかじめしておくこと)。
  2. keysvalsdf[ラベル] に代入する。
  3. to_csv メソッドにファイルパスを渡し、CSVファイルをエクスポートする。

TextExtractor#export_csv

def export_csv(self, keys, vals, path):
    df = pd.DataFrame()
    df["項目"] = keys
    df["値"] = vals
    df.to_csv(path)

4. ユニットテスト

  • TestTextExtractor#setUpGoogle Chromeを起動し、ログイン - Webスクレイピング入門ページで指定されたユーザー名、パスワードでログインする。
  • TestTextExtractor#test_get_lecturer_infoTextExtractor#get_lecturer_info講師情報 - Webスクレイピング入門ページから取得した情報で生成したディクショナリーが適切なキー、値を含んでいるかを検証する。
  • TestTextExtractor#test_export_csvTextExtractor#export_csvCSVファイルを指定したパスにエクスポートしているか検証する。

test/test_text_extractor.py

import unittest
import sys
sys.path.append("../lib")
import os.path
from os import path
from text_extractor import TextExtractor

class TestTextExtractor(unittest.TestCase):
    def setUp(self):
        self.text_extractor = TextExtractor("https://scraping-for-beginner.herokuapp.com/login_page")
        self.text_extractor.login("imanishi", "kohei")

    def test_get_lecturer_info(self):
        profile, *_ = self.text_extractor.get_lecturer_info()
        self.assertEqual({
            "講師名": "今西 航平",
            "所属企業": "株式会社キカガク",
            "生年月日": "1994年7月15日",
            "出身": "千葉県",
            "趣味": "バスケットボール、読書、ガジェット集め"
        }, profile)

    def test_export_csv(self):
        _, keys, vals = self.text_extractor.get_lecturer_info()
        self.text_extractor.export_csv(keys, vals, "../csv/lecturer_info.csv")
        self.assertEqual(True, path.exists("../csv/lecturer_info.csv"))

if __name__ == "__main__":
    unittest.main()

5. ソースコード

oasis-forever/web_scraping_tutorial