【簡単】Pythonによるスクレイピングのサンプル

seleniumによるHTML解析を10倍速くした方法 Pythonの使い方

スクレイピングのサンプルです。使用しているライブラリは、ドライバを経由してブラウザを操作するSeleniumになります。読み込んだHTMLの解析も、Seleniumでできます。

Seleniumはブラウザを操作するため遅いのが欠点ですが、出来る事が多いのです。そのため、要求事項が増える可能性が高い場合、第一候補にしています。

  • URLを指定してHTMLの読み込み
  • onload()後のHTMLの解析
  • 特定のJavaScriptの実行
  • タグのクリック
  • ブラウザのスクロール
  • inputタグにテキストを入力
  • 画面のキャプチャ

同様のものに、PyppeteerやRequstsの派生であるRequst-HTMLがあり、どれを使っても同じことができます。ここでは、もっともポピュラーなSeleniumを使った、スクレイピングのサンプルを置いておきます。

Python+Seleniumのサンプル

C#+Requstsでスクレイピングをしていたのですが、Python+Seleniumを知ってからは、余りの簡単さに戻れなくなりなりました。

Requstsは、取得したHTMLを正規表現又は他のライブラリを使って行います。他のライブラリとは、lxml、BeautifulSoup、pyqueryです。この組み合わせで使ってもスクレイピングができますが、Seleniumを使えば、複数のライブラリを意識しせずにできます。

Chromeのドライバのインストール

windowsの場合になりますが、https://sites.google.com/a/chromium.org/chromedriver/downloadsにアクセスして、使っているChromeと同じバージョンのzipファイルをダウンロードします。zipからchromedriver.exeを取り出し、パスん取っているところにコピーすれば完了です。

【サンプル】seleniumでYahoo!知恵袋のデータを取得

Yahoo!知恵袋のコンピュータテクノロジーから、悩みのタイトルを取得するサンプルです。

from selenium import webdriver
from typing import List
import time


def read_yahoo(driver, result_list: List[dict]) -> None:

    tag_div_list = driver.find_elements_by_css_selector(
        "div.ClapLv3List_Chie-List__ListItem__y_P8W div.ClapLv2ListItem_Chie-ListItem__TextWrapper__IQDCF")		# ※1
    for item_tag in tag_div_list:
        item: dict = {}

        #   カテゴリー
        tag = item_tag.find_element_by_class_name("ClapLv2ListItem_Chie-ListItem__Category__2sWPa")
        item["category"] = tag.text

        #   タイトル
        tag = item_tag.find_element_by_tag_name("h2")
        item["title"] = tag.text

        #   時刻
        tag_div_list = item_tag.find_elements_by_css_selector(
            "div.ClapLv2ListItem_Chie-ListItem__InformationItem__VFoZL "
            "> div.ClapLv2ListItem_Chie-ListItem__InformationText__3ONxm")
        for tag_div in tag_div_list:
            if tag_div.get_attribute("title"):
                item["datetime"] = tag_div.text
                break

        result_list.append(item)


def main() -> None:
    result_list: List[dict] = []

    driver = webdriver.Chrome()
    driver.get('https://chiebukuro.yahoo.co.jp/category/2078297616/question/list?page=1')
    time.sleep(3)

    #	1ページ目を取得
    read_yahoo(driver, result_list)

    #	次へを押下し、ページ遷移後データを取得
    for _ in range(3):
        tag_list = driver.find_elements_by_css_selector(".ClapLv1Pagination_Chie-Pagination__Anchor__2zpKc")
        for tag in tag_list:
            if "次へ" == tag.text:
                tag.click()		#	※2
                time.sleep(3)
                break

        read_yahoo(driver, result_list)
        time.sleep(3)

    print(result_list)


if __name__ == '__main__':
    main()

長めのサンプルに見えますが、これの作成にかかった時間は、僅か30分です。そして、欲しいデータの取得に、cssセレクターを使用(上記※1)しているため、デザインが変更された場合の実装の変更は最小限に抑えられます。

また、計4ページ分のデータを取得していますが、次へを押下してページ遷移(上記※2)させています。URLのクエリによって読み込むページを指定する方法もあるのですが、クリックイベントにより提供側で何らかの統計を取っている可能性があるため、わざわざクリックしています。

【サンプル】WEBページをイメージ保存

ページの全てをpngにするものです。実は、Google Chromeには”Fire Shot”というサードパティ製の拡張機能により、同じことができます。しかし、複数のページをイメージするとなると、そのページ手動で表示しなければなりません。そこで作りました。

def get_page_size(target_url: str) -> (int, int):

    # ブラウザを起動してURLを開く
    driver = webdriver.Chrome()
    driver.get(target_url)

    w: int = driver.execute_script("return document.body.scrollWidth;")
    h: int = driver.execute_script("return document.body.scrollHeight;")

    #   ブラウザを閉じる
    driver.close()

    return w, h


# 指定サイズでページをイメージ化
def screenshot_size(target_url: str, file_path: str, w: int, h: int):

    # ウィンドウサイズを指定
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    options.add_argument(f'window-size={str(w)},{str(h)}')

    # 指定のウィンドウサイズでChromeを起動し、キャプチャ
    driver = webdriver.Chrome(options=options)
    driver.get(target_url)

    driver.save_screenshot(file_path)
    driver.close()


READ_URL: str = "https://yahoo.co.jp/"
SCREENSHOT_PATH: str = ".\\image.png"


if __name__ == '__main__':

    w, h = get_page_size(READ_URL)

    # 指定サイズでChromeを起動し画面をキャプチャ
    screenshot_size(READ_URL, SCREENSHOT_PATH, w, h)

最初にHTMLから表示領域のサイズを取得します。その後、取得したサイズでヘッドレスモードでブラウザを開き、全体をキャプチャしています。

しかし、Yahoo!のトップページは、スクロールする度に新たに記事が表示されます。上記の方法では、最初の表示サイズだけしかイメージ化されていないのです。

そこで、取られる方法としては、スクロールしてスクリーンショットを取り、再度スクロールしてスクリーンショットを取ってを繰り返しています。

def scroll_to_bottom(driver,file_path: str) -> None:

    pre_h: int = driver.execute_script("return document.body.scrollHeight")

    index: int = 0
    while True:
        driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")
        time.sleep(3)

        driver.save_screenshot(f"file_path{index}.png")

        crt_h: int = driver.execute_script("return document.body.scrollHeight")

        if crt_h == pre_h:
            break

        pre_h = crt_h
        index += 1

保存した全てのpngを縦に繋げれば、イメージ化の完了です。

インストール

pip install selenium

コメント

タイトルとURLをコピーしました