【Python】Seleniumで遅延ロード(画像)対応のスクレイピング

【Python】Seleniumで遅延ロード(画像)対応のスクレイピング

画像の遅延読み込み(Lazy Loading)に対応したWEBサイトのスクレイピングで考慮すべき点や実装例など。一般的なマナー、スクレイピング元の規約など、しっかりと確認した上で実装しましょう。

Python #Python#スクレイピング

【Python】Seleniumで遅延ロード(画像)対応のスクレイピング

サムネイル

画像の遅延読み込み(Lazy Loading)に対応したWEBサイトのスクレイピングで考慮すべき点や実装例など。一般的なマナー、スクレイピング元の規約など、しっかりと確認した上で実装しましょう。

更新日: 8/28/2025

前提

モダンなWebサイトでは、ページの初期読み込み時間を短縮するため、画像の遅延読み込み(Lazy Loading)が割と広めに採用されています。data-srcdata-original属性に実際のURLが設定されていたり、JavaScriptによってスクロール時にsrc属性に移動されるような仕組みだったりします。通常のrequestでは、JavaScript実行前のHTMLしか取得できないため、これらの画像URLを見逃してしまいます。

どんな時に使えるのか

  • 自社ECサイトの商品画像の一括取得・品質チェック
  • 社内ポートフォリオサイトの作品画像収集
  • 企業ギャラリーサイトの画像アーカイブ作成
  • 自社メディアサイトのコンテンツ監査
  • 社内システムの画像データ移行作業

【重要】スクレイピング実行前に確認しよう

1. 法的確認事項
- 対象サイトの利用規約・robots.txtを必ず確認
- 著作権・肖像権などの知的財産権を尊重
- 個人情報保護法(GDPR等)の遵守

2. 技術的マナー
- 適切な間隔(1-3秒)でのリクエスト実行
- User-Agentの適切な設定
- サーバー負荷を考慮した同時接続数の制限

3. 推奨される使用場面
- 自社サイト・関連サイトでの利用
- 明示的な許可を得たサイト
- パブリックAPI代替としての社内利用

注意:本コードは教育・研究目的および自社サイト運用での使用を想定しています

コードの内容

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
import random

def scroll_and_wait_for_images(driver, url, max_scrolls=5, scroll_pause_time=1.0, image_wait_time=2.0):
    """画像読み込み待機付きスクロール処理"""
    try:
        driver.get(url)
        
        # 初期読み込み完了を待機
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.TAG_NAME, "body"))
        )
        print(f"ページを読み込み完了: {url}")
        
        # スクロール前の画像数を記録
        initial_img_count = len(driver.find_elements(By.TAG_NAME, "img"))
        print(f"初期画像数: {initial_img_count}個")
        
        last_height = driver.execute_script("return document.body.scrollHeight")
        scroll_count = 0
        
        while scroll_count < max_scrolls:
            # 段階的なスクロール(人間らしい動作でBot検出回避)
            scroll_steps = random.randint(3, 7)
            for step in range(scroll_steps):
                scroll_position = (step + 1) * (last_height / scroll_steps)
                driver.execute_script(f"window.scrollTo(0, {scroll_position});")
                time.sleep(random.uniform(0.2, 0.8))
            
            # 最下部までスクロール
            driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(scroll_pause_time)
            
            # 遅延読み込み画像の強制読み込み
            force_load_lazy_images(driver)
            time.sleep(image_wait_time)
            
            # 現在の画像数を確認
            current_img_count = len(driver.find_elements(By.TAG_NAME, "img"))
            print(f"スクロール {scroll_count + 1}: {current_img_count}個の画像")
            
            # ページ高さの変化を確認
            new_height = driver.execute_script("return document.body.scrollHeight")
            if new_height == last_height:
                print(f"スクロール完了 (回数: {scroll_count + 1})")
                break
                
            last_height = new_height
            scroll_count += 1
        
        return driver.page_source
        
    except Exception as e:
        print(f"スクロール処理エラー: {e}")
        return None

def force_load_lazy_images(driver):
    """遅延読み込み画像を強制的に読み込む"""
    try:
        # JavaScriptで遅延読み込み画像を即座に読み込み
        script = """
        // data-src属性を持つ画像を強制読み込み
        var lazyImages = document.querySelectorAll('img[data-src], img[data-original], img[data-lazy]');
        console.log('遅延読み込み画像を検出:', lazyImages.length, '個');
        
        lazyImages.forEach(function(img, index) {
            setTimeout(function() {
                if (img.dataset.src) {
                    img.src = img.dataset.src;
                    img.removeAttribute('data-src');
                } else if (img.dataset.original) {
                    img.src = img.dataset.original;
                    img.removeAttribute('data-original');
                } else if (img.dataset.lazy) {
                    img.src = img.dataset.lazy;
                    img.removeAttribute('data-lazy');
                }
            }, index * 50); // 50ms間隔で段階的に読み込み
        });
        
        // Intersection Observer APIを無効化(一部サイト用)
        if (window.IntersectionObserver) {
            window.IntersectionObserver = function() {
                return {
                    observe: function() { console.log('IntersectionObserver.observe() - bypassed'); },
                    unobserve: function() { console.log('IntersectionObserver.unobserve() - bypassed'); },
                    disconnect: function() { console.log('IntersectionObserver.disconnect() - bypassed'); }
                };
            };
        }
        """
        
        driver.execute_script(script)
        time.sleep(2.0)  # 画像読み込み完了を待機
        
    except Exception as e:
        print(f"遅延読み込み画像処理エラー: {e}")

# 使用例
def extract_images_from_page(url, output_file="extracted_images.txt"):
    """ページから画像URLを抽出してファイルに保存"""
    
    # Chrome設定(画像読み込み有効)
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    
    driver = webdriver.Chrome(options=chrome_options)
    
    try:
        # 画像読み込み待機付きスクロール
        page_source = scroll_and_wait_for_images(driver, url)
        
        if page_source:
            # BeautifulSoupで画像URL抽出
            soup = BeautifulSoup(page_source, 'html.parser')
            images = soup.find_all('img', src=True)
            
            image_urls = []
            for img in images:
                src = img.get('src')
                if src and not src.startswith('data:'):
                    if src.startswith('//'):
                        src = 'https:' + src
                    elif src.startswith('/'):
                        from urllib.parse import urljoin
                        src = urljoin(url, src)
                    image_urls.append(src)
            
            # 重複除去
            unique_urls = list(set(image_urls))
            
            # ファイルに保存
            with open(output_file, 'w', encoding='utf-8') as f:
                for url in unique_urls:
                    f.write(f"{url}\n")
            
            print(f"抽出完了: {len(unique_urls)}個の画像URLを {output_file} に保存")
            return unique_urls
        
    finally:
        driver.quit()

# 実行例
if __name__ == "__main__":
    # 注意: 実際の使用時には規約の確認などはしっかりと。
    target_url = "https://your-site.com/gallery"
    extract_images_from_page(target_url)

検索

検索条件に一致する記事が見つかりませんでした