🧭 Seleniumを使ってJavaScriptサイトをスクレイピング
JavaScriptで後から生成される要素は、requests+BeautifulSoupだけでは取得できません。
その場合は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_clickable+execute_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に掲載
この記事が役立ったら、ブックマーク&シェアをお願いします!
