MENU

Pythonでのスクレイピングの際に知っておくべきこと

以下の方向けの記事になります
  • Pythonでのスクレイピングの勉強を始めたばかりの方
  • Selenium、BeautifSoup、ChromeDriverを用いる方
  • スクレイピングのコードを書き始めたばかりの方

スクレイピングのコードを書く際につまった箇所、知っておけばよかったことをまとめました。

目次

基礎知識

そもそもSelenium、BeautifulSoup、ChromeDriverって何?

Seleniumはブラウザの制御、BeautifulSoupはHTMLコードからデータ抽出を行うライブラリ。

ChoromeDriverはSeleniumで制御されるブラウザとなります。

これらを用いることで、以下の様な流れでスクレイピングを行います。

  1. SeleniumでChoromeDriverを操作し、スクレイピングしたい情報のあるURLに移動。
  2. Seleniumで取得したHTMLコードをBeautifulSoupに渡す。
  3. BeautifulSoupを用いて必要な情報を取得する。
  4. 取得したデータの不要箇所の除去、文字コードの変換等、データのクレンジングを行う。
  5. クレンジング後のデータをcsv等の形式で出力したり、それをもとに追加の処理を行ったりする。

この様にしてデータを収集してもらいます。

どのサイトでもスクレイピングしていいの?

robots.txtに記載があるので、確認しましょう。

暗黙のルールとかも有るようなので、守るに越したことはありません。

Qiita
Webスクレイピングの注意事項一覧 - Qiita [2020/09/09追記] 本記事の内容は著作権法改正より前に記載されたものです。 最新の情報をご確認下さい。 * 著作権法の一部を改正する法律(平成30年法律第30号)について ...

ChromeにてCSS Selectorの取得、及び確認する方法

CSS Selectorとは

HTMLコードの何処を指すのか示す呪文みたいなやつです。

以下のようなHTMLコードの「読み込みたい!」のを指定する場合は「div > p」となります。

<div class="main">
    <p>読み込みたい!!!</p>
</div>

このような単純な構造であれば「div > p」と指定すればいいのですが、実際はなかなか難しいです。

何故なら、実際のサイトは複雑な構造となっているためです。

そこでChrome先生にお願いして取得してもらいましょう。

Chromeでの取得手順

Chromeの開発者ツールを用いることで、CSS Selectorの取得、及び確認が容易に行えます。

例としてGoogleのトップページにある、「Gmail」の要素を指すCSS Selectorを取得してみます。

STEP
Googleのトップページを開きます
STEP
Google ChromeにてF12キーを押す
開発者ツールが立ち上がります
STEP
カーソルマークのボタンをクリックし、「Gmail」にカーソルを移動
Ctrl + Shift + c でもOK
STEP
「Gmail」をクリックするとHTMLコードが表示されます
STEP
右クリックからCopy→CSS Selectorを選択

コピー出来たものが選択された要素を指定するCSS Selectorとなります。

確認手順

本当に正しいのか確認してみましょう。

この方法で取得したSelectorは悠長に感じますが、動けばいいので私は気にしません。

STEP
開発者ツールからConsoleを選択し、$$(“CSS Selector”)を入力する

CSS Selectorに適合する要素がリスト形式で表示される。

STEP
Enterで確定し、展開して要素リストを確認する

Gmailのロゴが選択されていることが確認できました。

SeleniumのTips

ブラウザの状況を読み取り、次の処理に移らせる

ブラウザの操作ではリンクやボタンを押してウェブサイトの読み込みやJavascriptを動作させます。

従って、クリックするべきリンクやボタンの読み込みを終えたタイミングで処理を行う必要があります。

どのタイミングで次の処理に進むかは、大きく分け次の2つの選択肢があります。

特定要素が読み込まれたタイミング
一定時間が経過したタイミング
  • 確実に挙動を制御できるため、
    何処でエラーが生じたか把握が容易
  • 無駄な待ち時間が生じないため、
    実行時間が短縮される
  • 読み込み状況がどうなろうと、時間経過後次に進む。
  • 無駄な待ち時間が生じる。
  • エラーの原因となる可能性が高いため、「最終手段」として用いる。

「特定要素が読み込まれたタイミング」で処理するほうが時短、精度向上に繋がるため推奨されます。

サンプルコードの「WebDriverWait()」の箇所が特定要素の確認コードとなります。

読み込みエラー時の処理

ウェブサイトの構造が変わったり、読み込みが遅いと想定通りの挙動を取らなくなります。

精度を高める必要があれば、「リトライ処理→失敗時に原因解明のための出力」機能の実装を推奨します。

サンプルコード

上記のブラウザ上の要素の確認、及びエラー処理を実装した、サンプルコードは以下の通りです。

import sys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException

def exist_check_CCS_Selector(driver, css_selector, wait_second=15, retries_count=3):
    """CSS Selectorが存在するか確認する

    Args:
        driver : webdriver.Chrome()
        css_selector ([string]): 検索するCSS Selector
        wait_second ([integer]): 待機時間. Defaults to 15.
        retries_count ([integer]): リトライ回数. Defaults to 3.
    """
    for _ in range(retries_count):
        try:
            # リトライさせる処理
            WebDriverWait(driver, wait_second).until(EC.visibility_of_element_located((By.CSS_SELECTOR, css_selector)))
        except TimeoutException as e:
            print('{}.url:{} retry'.format(e, driver.current_url))
        else:
            # 成功時forを抜ける
            break
    else:
        # リトライしても成功しなかった場合の処理
        # スクショやSelector、URL等を出力すると原因解明が容易となる

        # 見つからなかったSelectorを出力し、終了
        sys.exit('Cant find css selector {}'.format(css_selector))
    return

Beautiful SoupのTips

複数クラスを持つ要素の指定(find or select)

要素を検索するメソッドとして、findとselectがあります。

挙動が微妙に異なるため、取りあえずいくつか方法があることだけ認識しておきましょう。

違いの一例。

findを使用すると、「classA classB」の順番で持つ要素のみ取得できます。

    soup = scraping.make_beautifullSoup(driver)
    nodes = soup.find_all(class_='classA classB')

# <div class="classA classB">抽出されるよ</div>
# <div class="classB classA">抽出されない</div>

一方、selectを使用することで、順不同の要素が取得できます。

    soup = scraping.make_beautifullSoup(driver)
    nodes = soup.select('.classA.classB')

# <div class="classA classB">抽出されるよ</div>
# <div class="classB classA">抽出されるよ</div>

要素内の文字列取得(.textを使おう)

要素内の文字列を取得する方法として、「.string」と「.text」があります。

だいたい同じ挙動なのですが、stringだとHTML構造によっては「None」となり文字列が取得出来ません。

従いまして、「.text」を使用しましょう。

Chrome Driver

Chrome Driver設定

Chrome Driverは何故必要か

昨今のウェブサイトはJavascript等でページ内容(専門用語だとDOM)が変化するものが多々あります。

従って、単純にHTMLコードを入手しただけでは目的の情報が収集出来ないことがあります。

そこで実際にブラウザ上でJavaScriptを動かし、必要な情報を表示させるためにChrome Driverが必要となります。

Chrome DriverのVersion選択

現在使用しているGoogle Chromeと同じVersionを選択しましょう。

Choromeの設定(縦に並んだ→…) → ヘルプ → Google Chromeについて を選択すれば確認できます。

Chrome Driverの設置場所

PythonもしくはAnacondaのインストール時、環境変数のPathを通した場所に置きましょう。

Chrome DriverのTips

オプションを加えることでPC、及びスクレイピング先への負荷を抑制することが可能となります。

最後に自分のオプション設定を記載していますのでご参考ください。

Proxy(shadowsocks等)を使用する

Chrome Driverのoptionにて「add_argument(“–proxy-server=’direct://'”)」としない

中国IPだと蹴られることが多いので、自分の環境では使用していません。

ヘッドレスモードで動かない場合のUserAgent偽装方法

ウェブサイトによっては、ブラウザ情報(UserAgent)を確認するので上手くいかないことがあります。

ブラウザを起動させる場合は動くが、ヘッドレスモードでは動かない場合、UserAgentの偽装で解決するかも知れません。

そもそもスクレイピング但し、UAを偽装して大丈夫なのかはrobots.txtを確認しましょう。

HTMLを読み込み次第スクレイピングを実行させる(page_load_strategy = ‘eager’)

HTMLだけで欲しい情報が完結する場合、画像やCSSを読み込む必要がありません。

特に容量が大きい画像の読み込みを待たないことで、時間短縮、サーバー側への負荷軽減にも繋がります。

また、ページの読み込みが完了せず途中で止まるケースでも有効です。

Chromeオプション設定例

上記のTipsを踏まえた以下のコードを使用しています。

def conect_without_blowser(URL, weiting_CSS_selector, use_proxy=True, use_add_argument=True,
                           use_eager_mode=False):
    # ChoromeDriver
    op = Options()
    # ヘッドレスモード設定
    op.add_argument("--headless")
    op.add_argument("--disable-gpu")
    op.add_argument("--disable-extensions")
    op.add_argument("--start-maximized")
    # option設定
    if not use_proxy:
        print("proxy not use Mode")
        op.add_argument("--proxy-server='direct://'")
        op.add_argument("--proxy-bypass-list=*")
    if not use_add_argument:
        op.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36')  # 追加
    if use_eager_mode:
        op.set_capability("pageLoadStrategy", "eager")

    driver = webdriver.Chrome(chrome_options=op)

    # Seleniumでアクセス
    driver.get(URL)
    WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CSS_SELECTOR, weiting_CSS_selector)))

    return driver

その他

Windowsでの文字エンコード処理 (UTF-8→cp932変換処理)

Windowsにてcsvファイル等を出力する際、cp932に存在しない文字列が存在するとエラーとなります。

従って、cp932に存在しない文字列を除去する必要が出てきます。

以下のコードのutf8特殊文字除去の箇所で処理しています。

私はついでに無駄なスペース、改行コードの除去も行えるようにしています。

import re
def formatting_str_list(Info_list, use_break=False):
    """文字列リストのクレンジング

    Args:
        Info_list (list): 元の情報リスト
        use_break (bool, optional): 改行を残すか. Defaults to False.

    Returns:
        [list]: 書式を整えた情報リスト
    """
    retrun_list = []
    for string in Info_list:
        # utf8特殊文字除去
        format_str = string.encode('cp932', "ignore")
        format_str = format_str.decode('cp932')
        # 2つ以上のスペース(半角、全角)を半角スペース1つに置換
        format_str = re.sub('[  ]{2,}', ' ', format_str)
        # 改行を削除
        format_str = re.sub('^\n', '', format_str)
        # 頭スペース削除
        format_str = re.sub('^[  ]', '', format_str)
        if not use_break:
            format_str = format_str.replace('\n', '')
        retrun_list.append(format_str)

    return retrun_list
シェアしてくださいな
目次
閉じる