Seleniumを使ってJavaScriptサイトをスクレイピング


🧭 Seleniumを使ってJavaScriptサイトをスクレイピング

JavaScriptで後から生成される要素は、requestsBeautifulSoupだけでは取得できません。
その場合はSeleniumで実ブラウザを自動操作し、描画後のDOMからデータを抜き出します。


✅ 事前準備

pip install selenium webdriver-manager pandas

webdriver-managerを使うとChromeDriverの取得・更新が自動化され、パス設定が不要です。


🚀 最小サンプル:JS生成リストを取得

以下はJSで後から表示されるカード要素を想定した最小例です。
Seleniumでは明示的待機(WebDriverWait)で「要素が現れるまで待つ」のが基本です。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import pandas as pd

# --- ブラウザ起動(ヘッドレス) ---
opts = webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-gpu")
opts.add_argument("--no-sandbox")
opts.add_argument("--window-size=1280,2000")
opts.add_argument("--lang=ja-JP")
opts.add_argument("user-agent=Mozilla/5.0")

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=opts)
wait = WebDriverWait(driver, 15)

try:
    url = "https://quotes.toscrape.com/js/"  # JS生成デモサイト(例)
    driver.get(url)

    # JSで現れるカードを待つ
    cards = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".quote")))

    rows = []
    for c in cards:
        text = c.find_element(By.CSS_SELECTOR, ".text").text
        author = c.find_element(By.CSS_SELECTOR, ".author").text
        rows.append({"本文": text, "著者": author})

    pd.DataFrame(rows).to_csv("quotes.csv", index=False, encoding="utf-8-sig")
    print("✅ quotes.csv を保存しました。")

finally:
    driver.quit()

⏳ 明示的待機レシピ集(コピペOK)

目的
要素が現れるまで wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,".item")))
クリック可能になるまで btn = wait.until(EC.element_to_be_clickable((By.XPATH,"//button[text()='次へ']"))); btn.click()
テキストが含まれるまで wait.until(EC.text_to_be_present_in_element((By.ID,"status"),"完了"))
要素数が揃うまで wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR,".row")) >= 50)

📜 無限スクロール対応

import time

def scroll_to_bottom(driver, pause=1.0, max_loops=20):
    last = driver.execute_script("return document.body.scrollHeight")
    for _ in range(max_loops):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(pause)
        new = driver.execute_script("return document.body.scrollHeight")
        if new == last:
            break
        last = new

# 使い方
scroll_to_bottom(driver, pause=1.2, max_loops=30)
items = driver.find_elements(By.CSS_SELECTOR, ".card")
print("件数:", len(items))

🔁 ページネーション(次へを辿る)

rows = []
while True:
    # 現ページのカード取得
    wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".quote")))
    for q in driver.find_elements(By.CSS_SELECTOR, ".quote"):
        rows.append({
            "本文": q.find_element(By.CSS_SELECTOR, ".text").text,
            "著者": q.find_element(By.CSS_SELECTOR, ".author").text
        })

    # 次へ がなければ終了
    next_links = driver.find_elements(By.CSS_SELECTOR, "li.next a")
    if not next_links:
        break

    driver.execute_script("arguments[0].click();", next_links[0])

🖱 一覧→詳細:モーダル/別タブ対応

links = driver.find_elements(By.CSS_SELECTOR, ".post a.title")
base = driver.current_window_handle

for a in links[:5]:
    driver.execute_script("arguments[0].click();", a)    # クリック安定化
    # 別タブが開く場合はハンドル切替
    if len(driver.window_handles) > 1:
        for h in driver.window_handles:
            if h != base:
                driver.switch_to.window(h); break
    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "article")))
    title = driver.find_element(By.CSS_SELECTOR, "h1").text
    body = driver.find_element(By.CSS_SELECTOR, "article").text
    print(title[:30], len(body))
    driver.close()
    driver.switch_to.window(base)

💾 CSV保存&再利用

import pandas as pd
df = pd.DataFrame(rows)
df.to_csv("result.csv", index=False, encoding="utf-8-sig")

🛡️ 失敗しにくい設計(例外処理)

from selenium.common.exceptions import TimeoutException, NoSuchElementException

def safe_text(el, selector):
    try:
        return el.find_element(By.CSS_SELECTOR, selector).text
    except NoSuchElementException:
        return ""

try:
    el = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,".profile")))
except TimeoutException:
    print("⚠️ プロフィールが見つからずスキップ")

🧪 セレクタ選びのコツ

  • CSSセレクタ優先(短く高速)— .class / #id / div.item > a
  • テキスト一致や複雑条件はXPath//button[text()='OK'] / //a[contains(.,'詳細')]
  • 動的クラス(乱数付き)は親子関係やの順序で回避

🧷 ヘッドレス安定化Tips

  • --window-size=1280,2000でレイアウト差を抑える
  • クリックはelement_to_be_clickableexecute_script('arguments[0].click()')で安定
  • 短時間連打は避け、明示的待機を徹底

⚖️ マナー・法的注意(重要)

  • サイトの利用規約・robots.txt・著作権を遵守
  • アクセス頻度を抑え、サーバー負荷を最小化(スリープや待機を活用)
  • ログインが必要/個人情報領域の取得は不可。公開データのみ対象

🧩 仕上げ:ワンファイル雛形

# selenium_scrape_template.py
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import pandas as pd, time

opts = webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--window-size=1280,2000")

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=opts)
wait = WebDriverWait(driver, 15)
rows = []

try:
    driver.get("https://example.com")        # TODO: 取得対象URL
    wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR,".item")))  # TODO: セレクタ
    for el in driver.find_elements(By.CSS_SELECTOR, ".item"):
        title = el.find_element(By.CSS_SELECTOR, ".title").text                  # TODO
        href  = el.find_element(By.CSS_SELECTOR, "a").get_attribute("href")      # TODO
        rows.append({"タイトル": title, "リンク": href})

    pd.DataFrame(rows).to_csv("result.csv", index=False, encoding="utf-8-sig")
    print("✅ result.csv を出力:", len(rows), "件")

finally:
    driver.quit()

🏁 まとめ

  • SeleniumでJS描画後のDOMを直接取得
  • WebDriverWaitで安定化、無限スクロール/ページネーションにも対応
  • pandasでCSV保存し、分析・可視化やクラウド連携へ展開

“取れないページはSeleniumで”を覚えると、スクレイピングの適用範囲が一気に広がります。


🔗 次のステップ

  • scheduleで定期実行&自動更新
  • GoogleスプレッドシートAPIでクラウド反映
  • matplotlibでグラフ画像を生成してWordPressに掲載

この記事が役立ったら、ブックマーク&シェアをお願いします!

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