IT技術で仕事を減らしたい!

ITエンジニアのメモ+α

Python Seleniumスクレイピング

どうも、nippa です。

スクレイピングの必要性が出てきたので、Selenium で Web 操作を自動化したいと思います。

Web 開発でのモンキーテストにも使えるので、知っていて損はないかと思います。

Web サイトによってはスクレイピングが禁止されているところもありますので、ご注意ください。

環境

パッケージ管理

パッケージは poetry を使って管理していきます。利用するパッケージは

の2つです。

以前までは、PC にインストールされている Chrome のブラウザのメジャーバージョンを調べ、そのバージョンにあったドライバを用意する必要がありました。

chromedriver-binary-autoは自動でインストールされている Chrome のバージョン取得して、それに合うドライバをインストールしてくれます。

以下のコマンドでインストールを行います。

poetry add selenium chromedriver-binary-auto

スクレイピング

今回は自動で Google 検索を行って、検索内容について 100 件取得することを題材とします。

SeleniumGoogle.com の表示

まずは、Selenium の簡単な使い方になります。 url を指定して、Chrome で開き、5 秒スリーブごにブラウザを閉じるコードになります。

import time

import chromedriver_binary  # noqa
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


if __name__ == "__main__":
     base_url = "https://www.google.com/"

    url = f"{base_url}/"

    options = Options()

    # Browser
    driver = webdriver.Chrome(options=options)
    driver.get(url)

    time.sleep(5)

    driver.close()

SeleniumGoogle 検索

Google 検索で、「python selenium」を検索してみます。

スクレイピングでは html の構造を理解している必要があります。

Chrome であれば、Developer Tools を使うことで、html の構造を確認することができます。

また、beautifulsoup4 ライブラリを使えば html の構造ごと取得することもできます。

import sys
import time

import chromedriver_binary  # noqa
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

if __name__ == "__main__":
    base_url = "https://www.google.com/"

    url = f"{base_url}/"

    options = Options()

    driver = webdriver.Chrome(options=options)
    driver.get(url)

    name = "q"  # elementのname
    search_word = "python selenium"  # 検索するワード
    try:
        marker = driver.find_element(By.NAME, name)  # elementの取得
        marker.send_keys(search_word)  # elementに検索ワードの送信
        marker.send_keys(Keys.ENTER)  # elementにenterのkeyコマンドを送信

    except Exception as err:
        print(err)
        sys.exit(1)

    time.sleep(5)

    driver.close()

Google.com の検索用の input の name はqのため name を指定して、element を取得しています。

取得した element に検索するワードを送り、Enter key コマンドを送付して、検索を行います。

検索結果表示後、5秒間スリーブして、ブラウザを閉じるという動作になっています。

検索結果のタイトルと URL の取得

xpathを使って、検索結果のタイトルと URL を取得します。

google の検索結果の html の構造は以下のようになっています

...
<dvi id="search">
  <dvi>
    ...
    <a hred="リンク">
      <h3>タイトル</h3>
    </a>
    ...
  </dvi>
</dvi>

このときのタイトルを取得するためのxpathの書き方としては、//div[@id='search']//a/h3となります。

//は複数の element 意味します。

また親の element を取得する場合は..で一つ上のxpathを意味します。

import sys
import time

import chromedriver_binary  # noqa
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

if __name__ == "__main__":
    base_url = "https://www.google.com/"

    url = f"{base_url}/"

    options = Options()

    # Browser
    driver = webdriver.Chrome(options=options)
    driver.get(url)

    name = "q"
    search_word = "python selenium"

    try:
        marker = driver.find_element(By.NAME, name)
        marker.send_keys(search_word)
        marker.send_keys(Keys.ENTER)

        xpath = "//div[@id='search']//a/h3"  # 検索結果のtitleのxpath
        title_elements = driver.find_elements(By.XPATH, xpath)  # 結果の取得

    except Exception as err:
        print(err)
        sys.exit(1)

    for title_element in title_elements:
        if len(title_element.text) > 0:
            # 検索結果のtitleの親のxpathを取得
            href_element = title_element.find_element(By.XPATH, "..")
            # 出力
            print(
                f'titele={title_element.text.replace(" ...", "")} url={href_element.get_attribute("href")}'
            )

    time.sleep(5)

    driver.close()

これで検索 1 ページ目の検索結果のタイトル(一部)と URL を取得することができます。

複数のページの検索結果のタイトルと URL の取得

複数のページの検索結果を取得する場合、ページ移動を挟んで取得していきます。

ページ移動にはリンクの URL を取得して、そのページをロードする形にし、そのページが表示されたら、データを取得します。

xpath = "//div[@id='botstuff']//a[@id='pnnext']"
next_element = driver.find_element(By.XPATH, xpath)
next_page_url = next_element.get_attribute("href")
driver.get(next_page_url)

実際に pythonスクリプト化すると以下のようになります。

以下ではpython seleniumという検索ワードで 10 ページ分の検索結果をタイトル、url を表示するものになっています。

また、--headlessオプションを利用しています。

from typing import List, TypedDict, Optional
import sys
import time

import chromedriver_binary  # noqa
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys


class ResultData(TypedDict):
    title: Optional[str]
    url: Optional[str]


def get_title_and_url(driver: webdriver.Chrome) -> List[ResultData]:
    data: List[ResultData] = []

    try:
        xpath = "//div[@id='search']//a/h3"
        title_elements = driver.find_elements(By.XPATH, xpath)
    except Exception as err:
        print(err)
        sys.exit(1)

    for title_element in title_elements:
        if len(title_element.text) > 0:
            href_element = title_element.find_element(By.XPATH, "..")
            result_data: ResultData = {
                "title": title_element.text.replace(" ...", ""),
                "url": href_element.get_attribute("href"),
            }

            data += [result_data]

    return data

if __name__ == "__main__":
    base_url = "https://www.google.com/"

    url = f"{base_url}/"

    options = Options()
    options.add_argument("--headless")

    # Browser
    driver = webdriver.Chrome(options=options)
    driver.get(url)

    search_word = "python selenium"
    max_page = 10

    try:
        name = "q"
        marker = driver.find_element(By.NAME, name)
        marker.send_keys(search_word)
        marker.send_keys(Keys.ENTER)

    except Exception as err:
        print(err)
        sys.exit(1)

    data: List[ResultData] = []

    for i in range(max_page):
        data += get_title_and_url(driver)

        time.sleep(1)
        if i < max_page - 1:
            xpath = "//div[@id='botstuff']//a[@id='pnnext']"
            next_element = driver.find_element(By.XPATH, xpath)
            next_page_url = next_element.get_attribute("href")
            driver.get(next_page_url)

    driver.close()
    for _data in data:
        print(f'titele={_data["title"]} url={_data["url"]}')

感想

今回、Google の検索結果をスクレイピングselenium を使って書いてみました。

Web テスト用として利用する準備として、今回試しに使ってみています。

スクレイピングはサイトによっては禁止されていますので、サイトのルールに則ってください。

ではでは、また次回。