Oasistブログ

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

Python + Seleniumでウェブスクレイピング Vol.3 - 画像収集 -

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

Contents

1. 成果物

画像 - Webスクレイピング入門から画像を取得し、ローカルに保存する。

2. 実装

2-1. 画像取得

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

ImageCollector#__init__

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

f:id:oasist:20210129184742p:plain
Material Placeholder

ImageCollector#get_imagesfind_elements_by_class_name メソッドで .material-placeholder クラス要素のリストを取得し、以下の手順で各要素から画像を取得する(あらかじめ iourllib.requestPIL.Image をインポートするのを忘れないように)。

  1. 各要素の画像URLを挿入するための空のリストを生成する。
  2. for 文でループを回し、find_element_by_tag_name メソッドで各要素の img タグにアクセスする。そして、get_attribute("src") メソッドで画像URLを取得する。
  3. 画像ファイルを挿入するための空のリストを生成する。
  4. 画像URLにアクセスし、バイトライクなオブジェクトを読み込み、バイトオブジェクトを生成する。
  5. バイトオブジェクトを画像ファイルとして開く。
  6. 手順1で生成したリストに画像ファイルを追加する。

ImageCollector#get_images

def get_images(self):
    img_divs = self.chrome.find_elements_by_class_name("material-placeholder")
    img_urls = []
    for img_div in img_divs:
        img_urls.append(img_div.find_element_by_tag_name("img").get_attribute("src"))
    self.images = []
    for img_url in img_urls:
        f = io.BytesIO(request.urlopen(img_url).read())
        image = Image.open(f)
        self.images.append(image)
    return self.images

2-2. 画像保存

ImageCollector#get_images は画像リストから画像を1つずつ取り出し、指定されたパスに保存する。

ImageCollector#save_images

def save_images(self, path):
    i = 1
    for image in self.images:
        image.save(path.format(i))
        i += 1

3. ユニットテスト

  • TestImageCollector#setUpGoogle Chromeを立ち上げ、画像 - Webスクレイピング入門ページにアクセスする。
  • TestImageCollector#test_get_imagesImageCollector#get_images が取得した画像ファイルの数を検証する。
  • TestImageCollector#test_save_imagesImageCollector#save_images が指定したパスに画像が保存したかを検証する。

test/test_image_collector.py

import unittest
import sys
sys.path.append("../lib")
sys.path.append("../img")
import os.path
from os import path
from image_collector import ImageCollector

class TestImageCollector(unittest.TestCase):
    def setUp(self):
        self.image_collector = ImageCollector("https://scraping-for-beginner.herokuapp.com/image")

    def test_get_images(self):
        self.assertEqual(24, len(self.image_collector.get_images()))

    def test_save_images(self):
        self.image_collector.get_images()
        self.image_collector.save_images("../img/image_{:0=2}.jpg")
        self.assertEqual(True, path.exists("../img/image_01.jpg"))
        self.assertEqual(True, path.exists("../img/image_02.jpg"))
        self.assertEqual(True, path.exists("../img/image_03.jpg"))
        self.assertEqual(True, path.exists("../img/image_04.jpg"))
        self.assertEqual(True, path.exists("../img/image_05.jpg"))
        self.assertEqual(True, path.exists("../img/image_06.jpg"))
        self.assertEqual(True, path.exists("../img/image_07.jpg"))
        self.assertEqual(True, path.exists("../img/image_08.jpg"))
        self.assertEqual(True, path.exists("../img/image_09.jpg"))
        self.assertEqual(True, path.exists("../img/image_10.jpg"))
        self.assertEqual(True, path.exists("../img/image_11.jpg"))
        self.assertEqual(True, path.exists("../img/image_12.jpg"))
        self.assertEqual(True, path.exists("../img/image_13.jpg"))
        self.assertEqual(True, path.exists("../img/image_14.jpg"))
        self.assertEqual(True, path.exists("../img/image_15.jpg"))
        self.assertEqual(True, path.exists("../img/image_16.jpg"))
        self.assertEqual(True, path.exists("../img/image_17.jpg"))
        self.assertEqual(True, path.exists("../img/image_18.jpg"))
        self.assertEqual(True, path.exists("../img/image_19.jpg"))
        self.assertEqual(True, path.exists("../img/image_20.jpg"))
        self.assertEqual(True, path.exists("../img/image_21.jpg"))
        self.assertEqual(True, path.exists("../img/image_22.jpg"))
        self.assertEqual(True, path.exists("../img/image_23.jpg"))
        self.assertEqual(True, path.exists("../img/image_24.jpg"))

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

4. ソースコード

oasis-forever/web_scraping_tutorial

5. まとめ

JavaScriptのコーディングやテスティングフレームワークのE2EテストでDOM操作をした経験があれば、ウェブスクレイピングはすでに身近に感じるだろう。
私の場合は、SeleniumとCapybaraを使ってRSpecのSystem Specを経験済みなので、まったく難しいことはなかった。

PythonによるWebスクレイピング〜入門編〜 業務効率化への第一歩」で学んだことは至極基礎的なので、より複雑なケースに対応する場合は高度な教材や資料が必要になるだろう。
とはいえ、基本は最も重要なので、それはそれでよい学びになった。

注意点として、ウェブ上に公開されている一般サイトに対していたずらにウェブスクレイピングを行うとDos攻撃とみなされることがあるので、自身が所有権を持つサイトで行うことを基本としたい(参考)。